diff options
Diffstat (limited to 'net/bridge')
38 files changed, 1990 insertions, 987 deletions
diff --git a/net/bridge/Makefile b/net/bridge/Makefile index e85498b2f16..8590b942bff 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -5,7 +5,7 @@  obj-$(CONFIG_BRIDGE) += bridge.o  bridge-y	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ -			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ +			br_ioctl.o br_stp.o br_stp_bpdu.o \  			br_stp_if.o br_stp_timer.o br_netlink.o  bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o @@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o  bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o -obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ +obj-$(CONFIG_NETFILTER) += netfilter/ diff --git a/net/bridge/br.c b/net/bridge/br.c index ba780cc8e51..1a755a1e541 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -22,14 +22,127 @@  #include "br_private.h" -static const struct stp_proto br_stp_proto = { -	.rcv	= br_stp_rcv, +/* + * Handle changes in state of network devices enslaved to a bridge. + * + * Note: don't care about up/down if bridge itself is down, because + *     port state is checked when bridge is brought up. + */ +static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); +	struct net_bridge_port *p; +	struct net_bridge *br; +	bool changed_addr; +	int err; + +	/* register of bridge completed, add sysfs entries */ +	if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { +		br_sysfs_addbr(dev); +		return NOTIFY_DONE; +	} + +	/* not a port of a bridge */ +	p = br_port_get_rtnl(dev); +	if (!p) +		return NOTIFY_DONE; + +	br = p->br; + +	switch (event) { +	case NETDEV_CHANGEMTU: +		dev_set_mtu(br->dev, br_min_mtu(br)); +		break; + +	case NETDEV_CHANGEADDR: +		spin_lock_bh(&br->lock); +		br_fdb_changeaddr(p, dev->dev_addr); +		changed_addr = br_stp_recalculate_bridge_id(br); +		spin_unlock_bh(&br->lock); + +		if (changed_addr) +			call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); + +		break; + +	case NETDEV_CHANGE: +		br_port_carrier_check(p); +		break; + +	case NETDEV_FEAT_CHANGE: +		netdev_update_features(br->dev); +		break; + +	case NETDEV_DOWN: +		spin_lock_bh(&br->lock); +		if (br->dev->flags & IFF_UP) +			br_stp_disable_port(p); +		spin_unlock_bh(&br->lock); +		break; + +	case NETDEV_UP: +		if (netif_running(br->dev) && netif_oper_up(dev)) { +			spin_lock_bh(&br->lock); +			br_stp_enable_port(p); +			spin_unlock_bh(&br->lock); +		} +		break; + +	case NETDEV_UNREGISTER: +		br_del_if(br, dev); +		break; + +	case NETDEV_CHANGENAME: +		err = br_sysfs_renameif(p); +		if (err) +			return notifier_from_errno(err); +		break; + +	case NETDEV_PRE_TYPE_CHANGE: +		/* Forbid underlaying device to change its type. */ +		return NOTIFY_BAD; + +	case NETDEV_RESEND_IGMP: +		/* Propagate to master device */ +		call_netdevice_notifiers(event, br->dev); +		break; +	} + +	/* Events that may cause spanning tree to refresh */ +	if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || +	    event == NETDEV_CHANGE || event == NETDEV_DOWN) +		br_ifinfo_notify(RTM_NEWLINK, p); + +	return NOTIFY_DONE; +} + +static struct notifier_block br_device_notifier = { +	.notifier_call = br_device_event  }; +static void __net_exit br_net_exit(struct net *net) +{ +	struct net_device *dev; +	LIST_HEAD(list); + +	rtnl_lock(); +	for_each_netdev(net, dev) +		if (dev->priv_flags & IFF_EBRIDGE) +			br_dev_delete(dev, &list); + +	unregister_netdevice_many(&list); +	rtnl_unlock(); + +} +  static struct pernet_operations br_net_ops = {  	.exit	= br_net_exit,  }; +static const struct stp_proto br_stp_proto = { +	.rcv	= br_stp_rcv, +}; +  static int __init br_init(void)  {  	int err; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ca04163635d..568cccd39a3 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -32,7 +32,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)  	const unsigned char *dest = skb->data;  	struct net_bridge_fdb_entry *dst;  	struct net_bridge_mdb_entry *mdst; -	struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); +	struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);  	u16 vid = 0;  	rcu_read_lock(); @@ -49,14 +49,14 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)  	brstats->tx_bytes += skb->len;  	u64_stats_update_end(&brstats->syncp); -	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) -		goto out; -  	BR_INPUT_SKB_CB(skb)->brdev = dev;  	skb_reset_mac_header(skb);  	skb_pull(skb, ETH_HLEN); +	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) +		goto out; +  	if (is_broadcast_ether_addr(dest))  		br_flood_deliver(br, skb, false);  	else if (is_multicast_ether_addr(dest)) { @@ -64,7 +64,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)  			br_flood_deliver(br, skb, false);  			goto out;  		} -		if (br_multicast_rcv(br, NULL, skb)) { +		if (br_multicast_rcv(br, NULL, skb, vid)) {  			kfree_skb(skb);  			goto out;  		} @@ -89,7 +89,7 @@ static int br_dev_init(struct net_device *dev)  {  	struct net_bridge *br = netdev_priv(dev); -	br->stats = alloc_percpu(struct br_cpu_netstats); +	br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);  	if (!br->stats)  		return -ENOMEM; @@ -112,6 +112,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)  {  } +static void br_dev_change_rx_flags(struct net_device *dev, int change) +{ +	if (change & IFF_PROMISC) +		br_manage_promisc(netdev_priv(dev)); +} +  static int br_dev_stop(struct net_device *dev)  {  	struct net_bridge *br = netdev_priv(dev); @@ -128,17 +134,17 @@ static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev,  						struct rtnl_link_stats64 *stats)  {  	struct net_bridge *br = netdev_priv(dev); -	struct br_cpu_netstats tmp, sum = { 0 }; +	struct pcpu_sw_netstats tmp, sum = { 0 };  	unsigned int cpu;  	for_each_possible_cpu(cpu) {  		unsigned int start; -		const struct br_cpu_netstats *bstats +		const struct pcpu_sw_netstats *bstats  			= per_cpu_ptr(br->stats, cpu);  		do { -			start = u64_stats_fetch_begin_bh(&bstats->syncp); +			start = u64_stats_fetch_begin_irq(&bstats->syncp);  			memcpy(&tmp, bstats, sizeof(tmp)); -		} while (u64_stats_fetch_retry_bh(&bstats->syncp, start)); +		} while (u64_stats_fetch_retry_irq(&bstats->syncp, start));  		sum.tx_bytes   += tmp.tx_bytes;  		sum.tx_packets += tmp.tx_packets;  		sum.rx_bytes   += tmp.rx_bytes; @@ -180,8 +186,7 @@ static int br_set_mac_address(struct net_device *dev, void *p)  	spin_lock_bh(&br->lock);  	if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) { -		memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); -		br_fdb_change_mac_address(br, addr->sa_data); +		/* Mac address will be changed in br_stp_change_bridge_id(). */  		br_stp_change_bridge_id(br, addr->sa_data);  	}  	spin_unlock_bh(&br->lock); @@ -219,8 +224,34 @@ static void br_netpoll_cleanup(struct net_device *dev)  		br_netpoll_disable(p);  } -static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, -			    gfp_t gfp) +static int __br_netpoll_enable(struct net_bridge_port *p) +{ +	struct netpoll *np; +	int err; + +	np = kzalloc(sizeof(*p->np), GFP_KERNEL); +	if (!np) +		return -ENOMEM; + +	err = __netpoll_setup(np, p->dev); +	if (err) { +		kfree(np); +		return err; +	} + +	p->np = np; +	return err; +} + +int br_netpoll_enable(struct net_bridge_port *p) +{ +	if (!p->br->dev->npinfo) +		return 0; + +	return __br_netpoll_enable(p); +} + +static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)  {  	struct net_bridge *br = netdev_priv(dev);  	struct net_bridge_port *p; @@ -229,7 +260,7 @@ static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni,  	list_for_each_entry(p, &br->port_list, list) {  		if (!p->dev)  			continue; -		err = br_netpoll_enable(p, gfp); +		err = __br_netpoll_enable(p);  		if (err)  			goto fail;  	} @@ -242,28 +273,6 @@ fail:  	goto out;  } -int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) -{ -	struct netpoll *np; -	int err; - -	if (!p->br->dev->npinfo) -		return 0; - -	np = kzalloc(sizeof(*p->np), gfp); -	if (!np) -		return -ENOMEM; - -	err = __netpoll_setup(np, p->dev, gfp); -	if (err) { -		kfree(np); -		return err; -	} - -	p->np = np; -	return err; -} -  void br_netpoll_disable(struct net_bridge_port *p)  {  	struct netpoll *np = p->np; @@ -306,6 +315,7 @@ static const struct net_device_ops br_netdev_ops = {  	.ndo_get_stats64	 = br_get_stats64,  	.ndo_set_mac_address	 = br_set_mac_address,  	.ndo_set_rx_mode	 = br_dev_set_multicast_list, +	.ndo_change_rx_flags	 = br_dev_change_rx_flags,  	.ndo_change_mtu		 = br_change_mtu,  	.ndo_do_ioctl		 = br_dev_ioctl,  #ifdef CONFIG_NET_POLL_CONTROLLER @@ -345,14 +355,15 @@ void br_dev_setup(struct net_device *dev)  	dev->netdev_ops = &br_netdev_ops;  	dev->destructor = br_dev_free; -	SET_ETHTOOL_OPS(dev, &br_ethtool_ops); +	dev->ethtool_ops = &br_ethtool_ops;  	SET_NETDEV_DEVTYPE(dev, &br_type);  	dev->tx_queue_len = 0;  	dev->priv_flags = IFF_EBRIDGE;  	dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL | -			NETIF_F_HW_VLAN_CTAG_TX; -	dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX; +			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; +	dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | +			   NETIF_F_HW_VLAN_STAG_TX;  	dev->vlan_features = COMMON_FEATURES;  	br->dev = dev; @@ -363,10 +374,11 @@ void br_dev_setup(struct net_device *dev)  	br->bridge_id.prio[0] = 0x80;  	br->bridge_id.prio[1] = 0x00; -	memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN); +	ether_addr_copy(br->group_addr, eth_reserved_addr_base);  	br->stp_enabled = BR_NO_STP;  	br->group_fwd_mask = BR_GROUPFWD_DEFAULT; +	br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;  	br->designated_root = br->bridge_id;  	br->bridge_max_age = br->max_age = 20 * HZ; @@ -377,4 +389,5 @@ void br_dev_setup(struct net_device *dev)  	br_netfilter_rtable_init(br);  	br_stp_timer_init(br);  	br_multicast_init(br); +	br_vlan_init(br);  } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index ffd5874f259..b524c36c127 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -27,6 +27,9 @@  #include "br_private.h"  static struct kmem_cache *br_fdb_cache __read_mostly; +static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, +					     const unsigned char *addr, +					     __u16 vid);  static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,  		      const unsigned char *addr, u16 vid);  static void fdb_notify(struct net_bridge *br, @@ -82,18 +85,114 @@ static void fdb_rcu_free(struct rcu_head *head)  	kmem_cache_free(br_fdb_cache, ent);  } +/* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports + * are then updated with the new information. + * Called under RTNL. + */ +static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr) +{ +	int err; +	struct net_bridge_port *p, *tmp; + +	ASSERT_RTNL(); + +	list_for_each_entry(p, &br->port_list, list) { +		if (!br_promisc_port(p)) { +			err = dev_uc_add(p->dev, addr); +			if (err) +				goto undo; +		} +	} + +	return; +undo: +	list_for_each_entry(tmp, &br->port_list, list) { +		if (tmp == p) +			break; +		if (!br_promisc_port(tmp)) +			dev_uc_del(tmp->dev, addr); +	} +} + +/* When a static FDB entry is deleted, the HW address from that entry is + * also removed from the bridge private HW address list and updates all + * the ports with needed information. + * Called under RTNL. + */ +static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) +{ +	struct net_bridge_port *p; + +	ASSERT_RTNL(); + +	list_for_each_entry(p, &br->port_list, list) { +		if (!br_promisc_port(p)) +			dev_uc_del(p->dev, addr); +	} +} +  static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)  { +	if (f->is_static) +		fdb_del_hw(br, f->addr.addr); +  	hlist_del_rcu(&f->hlist);  	fdb_notify(br, f, RTM_DELNEIGH);  	call_rcu(&f->rcu, fdb_rcu_free);  } +/* Delete a local entry if no other port had the same address. */ +static void fdb_delete_local(struct net_bridge *br, +			     const struct net_bridge_port *p, +			     struct net_bridge_fdb_entry *f) +{ +	const unsigned char *addr = f->addr.addr; +	u16 vid = f->vlan_id; +	struct net_bridge_port *op; + +	/* Maybe another port has same hw addr? */ +	list_for_each_entry(op, &br->port_list, list) { +		if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && +		    (!vid || nbp_vlan_find(op, vid))) { +			f->dst = op; +			f->added_by_user = 0; +			return; +		} +	} + +	/* Maybe bridge device has same hw addr? */ +	if (p && ether_addr_equal(br->dev->dev_addr, addr) && +	    (!vid || br_vlan_find(br, vid))) { +		f->dst = NULL; +		f->added_by_user = 0; +		return; +	} + +	fdb_delete(br, f); +} + +void br_fdb_find_delete_local(struct net_bridge *br, +			      const struct net_bridge_port *p, +			      const unsigned char *addr, u16 vid) +{ +	struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; +	struct net_bridge_fdb_entry *f; + +	spin_lock_bh(&br->hash_lock); +	f = fdb_find(head, addr, vid); +	if (f && f->is_local && !f->added_by_user && f->dst == p) +		fdb_delete_local(br, p, f); +	spin_unlock_bh(&br->hash_lock); +} +  void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)  {  	struct net_bridge *br = p->br; -	bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false; +	struct net_port_vlans *pv = nbp_get_vlan_info(p); +	bool no_vlan = !pv;  	int i; +	u16 vid;  	spin_lock_bh(&br->hash_lock); @@ -104,38 +203,34 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)  			struct net_bridge_fdb_entry *f;  			f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); -			if (f->dst == p && f->is_local) { -				/* maybe another port has same hw addr? */ -				struct net_bridge_port *op; -				u16 vid = f->vlan_id; -				list_for_each_entry(op, &br->port_list, list) { -					if (op != p && -					    ether_addr_equal(op->dev->dev_addr, -							     f->addr.addr) && -					    nbp_vlan_find(op, vid)) { -						f->dst = op; -						goto insert; -					} -				} - +			if (f->dst == p && f->is_local && !f->added_by_user) {  				/* delete old one */ -				fdb_delete(br, f); -insert: -				/* insert new address,  may fail if invalid -				 * address or dup. -				 */ -				fdb_insert(br, p, newaddr, vid); +				fdb_delete_local(br, p, f);  				/* if this port has no vlan information  				 * configured, we can safely be done at  				 * this point.  				 */  				if (no_vlan) -					goto done; +					goto insert;  			}  		}  	} +insert: +	/* insert new address,  may fail if invalid address or dup. */ +	fdb_insert(br, p, newaddr, 0); + +	if (no_vlan) +		goto done; + +	/* Now add entries for every VLAN configured on the port. +	 * This function runs under RTNL so the bitmap will not change +	 * from under us. +	 */ +	for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) +		fdb_insert(br, p, newaddr, vid); +  done:  	spin_unlock_bh(&br->hash_lock);  } @@ -146,10 +241,12 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)  	struct net_port_vlans *pv;  	u16 vid = 0; +	spin_lock_bh(&br->hash_lock); +  	/* If old entry was unassociated with any port, then delete it. */  	f = __br_fdb_get(br, br->dev->dev_addr, 0);  	if (f && f->is_local && !f->dst) -		fdb_delete(br, f); +		fdb_delete_local(br, NULL, f);  	fdb_insert(br, NULL, newaddr, 0); @@ -159,14 +256,16 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)  	 */  	pv = br_get_vlan_info(br);  	if (!pv) -		return; +		goto out;  	for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) {  		f = __br_fdb_get(br, br->dev->dev_addr, vid);  		if (f && f->is_local && !f->dst) -			fdb_delete(br, f); +			fdb_delete_local(br, NULL, f);  		fdb_insert(br, NULL, newaddr, vid);  	} +out: +	spin_unlock_bh(&br->hash_lock);  }  void br_fdb_cleanup(unsigned long _data) @@ -235,25 +334,11 @@ void br_fdb_delete_by_port(struct net_bridge *br,  			if (f->is_static && !do_all)  				continue; -			/* -			 * if multiple ports all have the same device address -			 * then when one port is deleted, assign -			 * the local entry to other port -			 */ -			if (f->is_local) { -				struct net_bridge_port *op; -				list_for_each_entry(op, &br->port_list, list) { -					if (op != p && -					    ether_addr_equal(op->dev->dev_addr, -							     f->addr.addr)) { -						f->dst = op; -						goto skip_delete; -					} -				} -			} -			fdb_delete(br, f); -		skip_delete: ; +			if (f->is_local) +				fdb_delete_local(br, p, f); +			else +				fdb_delete(br, f);  		}  	}  	spin_unlock_bh(&br->hash_lock); @@ -397,6 +482,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,  		fdb->vlan_id = vid;  		fdb->is_local = 0;  		fdb->is_static = 0; +		fdb->added_by_user = 0;  		fdb->updated = fdb->used = jiffies;  		hlist_add_head_rcu(&fdb->hlist, head);  	} @@ -430,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,  		return -ENOMEM;  	fdb->is_local = fdb->is_static = 1; +	fdb_add_hw(br, addr);  	fdb_notify(br, fdb, RTM_NEWNEIGH);  	return 0;  } @@ -447,10 +534,11 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,  }  void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, -		   const unsigned char *addr, u16 vid) +		   const unsigned char *addr, u16 vid, bool added_by_user)  {  	struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];  	struct net_bridge_fdb_entry *fdb; +	bool fdb_modified = false;  	/* some users want to always flood. */  	if (hold_time(br) == 0) @@ -471,15 +559,25 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,  					source->dev->name);  		} else {  			/* fastpath: update of existing entry */ -			fdb->dst = source; +			if (unlikely(source != fdb->dst)) { +				fdb->dst = source; +				fdb_modified = true; +			}  			fdb->updated = jiffies; +			if (unlikely(added_by_user)) +				fdb->added_by_user = 1; +			if (unlikely(fdb_modified)) +				fdb_notify(br, fdb, RTM_NEWNEIGH);  		}  	} else {  		spin_lock(&br->hash_lock);  		if (likely(!fdb_find(head, addr, vid))) {  			fdb = fdb_create(head, source, addr, vid); -			if (fdb) +			if (fdb) { +				if (unlikely(added_by_user)) +					fdb->added_by_user = 1;  				fdb_notify(br, fdb, RTM_NEWNEIGH); +			}  		}  		/* else  we lose race and someone else inserts  		 * it first, don't bother updating @@ -524,6 +622,8 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,  	if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr))  		goto nla_put_failure; +	if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex)) +		goto nla_put_failure;  	ci.ndm_used	 = jiffies_to_clock_t(now - fdb->used);  	ci.ndm_confirmed = 0;  	ci.ndm_updated	 = jiffies_to_clock_t(now - fdb->updated); @@ -545,6 +645,7 @@ static inline size_t fdb_nlmsg_size(void)  {  	return NLMSG_ALIGN(sizeof(struct ndmsg))  		+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */ +		+ nla_total_size(sizeof(u32)) /* NDA_MASTER */  		+ nla_total_size(sizeof(u16)) /* NDA_VLAN */  		+ nla_total_size(sizeof(struct nda_cacheinfo));  } @@ -570,8 +671,7 @@ static void fdb_notify(struct net_bridge *br,  	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);  	return;  errout: -	if (err < 0) -		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); +	rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);  }  /* Dump information about entries, in response to GETNEIGH */ @@ -638,16 +738,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,  	}  	if (fdb_to_nud(fdb) != state) { -		if (state & NUD_PERMANENT) -			fdb->is_local = fdb->is_static = 1; -		else if (state & NUD_NOARP) { +		if (state & NUD_PERMANENT) { +			fdb->is_local = 1; +			if (!fdb->is_static) { +				fdb->is_static = 1; +				fdb_add_hw(br, addr); +			} +		} else if (state & NUD_NOARP) { +			fdb->is_local = 0; +			if (!fdb->is_static) { +				fdb->is_static = 1; +				fdb_add_hw(br, addr); +			} +		} else {  			fdb->is_local = 0; -			fdb->is_static = 1; -		} else -			fdb->is_local = fdb->is_static = 0; +			if (fdb->is_static) { +				fdb->is_static = 0; +				fdb_del_hw(br, addr); +			} +		}  		modified = true;  	} +	fdb->added_by_user = 1;  	fdb->used = jiffies;  	if (modified) { @@ -665,7 +778,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,  	if (ndm->ndm_flags & NTF_USE) {  		rcu_read_lock(); -		br_fdb_update(p->br, p, addr, vid); +		br_fdb_update(p->br, p, addr, vid, true);  		rcu_read_unlock();  	} else {  		spin_lock_bh(&p->br->hash_lock); @@ -700,7 +813,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],  		vid = nla_get_u16(tb[NDA_VLAN]); -		if (vid >= VLAN_N_VID) { +		if (!vid || vid >= VLAN_VID_MASK) {  			pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",  				vid);  			return -EINVAL; @@ -750,8 +863,7 @@ out:  	return err;  } -int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, -		       u16 vlan) +static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vlan)  {  	struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];  	struct net_bridge_fdb_entry *fdb; @@ -794,7 +906,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],  		vid = nla_get_u16(tb[NDA_VLAN]); -		if (vid >= VLAN_N_VID) { +		if (!vid || vid >= VLAN_VID_MASK) {  			pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",  				vid);  			return -EINVAL; @@ -834,3 +946,59 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],  out:  	return err;  } + +int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p) +{ +	struct net_bridge_fdb_entry *fdb, *tmp; +	int i; +	int err; + +	ASSERT_RTNL(); + +	for (i = 0; i < BR_HASH_SIZE; i++) { +		hlist_for_each_entry(fdb, &br->hash[i], hlist) { +			/* We only care for static entries */ +			if (!fdb->is_static) +				continue; + +			err = dev_uc_add(p->dev, fdb->addr.addr); +			if (err) +				goto rollback; +		} +	} +	return 0; + +rollback: +	for (i = 0; i < BR_HASH_SIZE; i++) { +		hlist_for_each_entry(tmp, &br->hash[i], hlist) { +			/* If we reached the fdb that failed, we can stop */ +			if (tmp == fdb) +				break; + +			/* We only care for static entries */ +			if (!tmp->is_static) +				continue; + +			dev_uc_del(p->dev, tmp->addr.addr); +		} +	} +	return err; +} + +void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) +{ +	struct net_bridge_fdb_entry *fdb; +	int i; + +	ASSERT_RTNL(); + +	for (i = 0; i < BR_HASH_SIZE; i++) { +		hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) { +			/* We only care for static entries */ +			if (!fdb->is_static) +				continue; + +			dev_uc_del(p->dev, fdb->addr.addr); +		} +	} +} diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 4b81b147178..056b67b0e27 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -26,25 +26,20 @@ static int deliver_clone(const struct net_bridge_port *prev,  			 void (*__packet_hook)(const struct net_bridge_port *p,  					       struct sk_buff *skb)); -/* Don't forward packets to originating port or forwarding diasabled */ +/* Don't forward packets to originating port or forwarding disabled */  static inline int should_deliver(const struct net_bridge_port *p,  				 const struct sk_buff *skb)  { -	return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && +	return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&  		br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) && -		p->state == BR_STATE_FORWARDING); -} - -static inline unsigned int packet_length(const struct sk_buff *skb) -{ -	return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0); +		p->state == BR_STATE_FORWARDING;  }  int br_dev_queue_push_xmit(struct sk_buff *skb)  {  	/* ip_fragment doesn't copy the MAC header */  	if (nf_bridge_maybe_copy_header(skb) || -	    (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) { +	    !is_skb_forwardable(skb->dev, skb)) {  		kfree_skb(skb);  	} else {  		skb_push(skb, ETH_HLEN); @@ -71,7 +66,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)  	skb->dev = to->dev;  	if (unlikely(netpoll_tx_running(to->br->dev))) { -		if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) +		if (!is_skb_forwardable(skb->dev, skb))  			kfree_skb(skb);  		else {  			skb_push(skb, ETH_HLEN); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index c41d5fbb91d..3eca3fdf8fe 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -61,7 +61,7 @@ static int port_cost(struct net_device *dev)  } -/* Check for port carrier transistions. */ +/* Check for port carrier transitions. */  void br_port_carrier_check(struct net_bridge_port *p)  {  	struct net_device *dev = p->dev; @@ -85,6 +85,111 @@ void br_port_carrier_check(struct net_bridge_port *p)  	spin_unlock_bh(&br->lock);  } +static void br_port_set_promisc(struct net_bridge_port *p) +{ +	int err = 0; + +	if (br_promisc_port(p)) +		return; + +	err = dev_set_promiscuity(p->dev, 1); +	if (err) +		return; + +	br_fdb_unsync_static(p->br, p); +	p->flags |= BR_PROMISC; +} + +static void br_port_clear_promisc(struct net_bridge_port *p) +{ +	int err; + +	/* Check if the port is already non-promisc or if it doesn't +	 * support UNICAST filtering.  Without unicast filtering support +	 * we'll end up re-enabling promisc mode anyway, so just check for +	 * it here. +	 */ +	if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT)) +		return; + +	/* Since we'll be clearing the promisc mode, program the port +	 * first so that we don't have interruption in traffic. +	 */ +	err = br_fdb_sync_static(p->br, p); +	if (err) +		return; + +	dev_set_promiscuity(p->dev, -1); +	p->flags &= ~BR_PROMISC; +} + +/* When a port is added or removed or when certain port flags + * change, this function is called to automatically manage + * promiscuity setting of all the bridge ports.  We are always called + * under RTNL so can skip using rcu primitives. + */ +void br_manage_promisc(struct net_bridge *br) +{ +	struct net_bridge_port *p; +	bool set_all = false; + +	/* If vlan filtering is disabled or bridge interface is placed +	 * into promiscuous mode, place all ports in promiscuous mode. +	 */ +	if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br)) +		set_all = true; + +	list_for_each_entry(p, &br->port_list, list) { +		if (set_all) { +			br_port_set_promisc(p); +		} else { +			/* If the number of auto-ports is <= 1, then all other +			 * ports will have their output configuration +			 * statically specified through fdbs.  Since ingress +			 * on the auto-port becomes forwarding/egress to other +			 * ports and egress configuration is statically known, +			 * we can say that ingress configuration of the +			 * auto-port is also statically known. +			 * This lets us disable promiscuous mode and write +			 * this config to hw. +			 */ +			if (br->auto_cnt == 0 || +			    (br->auto_cnt == 1 && br_auto_port(p))) +				br_port_clear_promisc(p); +			else +				br_port_set_promisc(p); +		} +	} +} + +static void nbp_update_port_count(struct net_bridge *br) +{ +	struct net_bridge_port *p; +	u32 cnt = 0; + +	list_for_each_entry(p, &br->port_list, list) { +		if (br_auto_port(p)) +			cnt++; +	} +	if (br->auto_cnt != cnt) { +		br->auto_cnt = cnt; +		br_manage_promisc(br); +	} +} + +static void nbp_delete_promisc(struct net_bridge_port *p) +{ +	/* If port is currently promiscuous, unset promiscuity. +	 * Otherwise, it is a static port so remove all addresses +	 * from it. +	 */ +	dev_set_allmulti(p->dev, -1); +	if (br_promisc_port(p)) +		dev_set_promiscuity(p->dev, -1); +	else +		br_fdb_unsync_static(p->br, p); +} +  static void release_nbp(struct kobject *kobj)  {  	struct net_bridge_port *p @@ -133,7 +238,7 @@ static void del_nbp(struct net_bridge_port *p)  	sysfs_remove_link(br->ifobj, p->dev->name); -	dev_set_promiscuity(dev, -1); +	nbp_delete_promisc(p);  	spin_lock_bh(&br->lock);  	br_stp_disable_port(p); @@ -141,10 +246,11 @@ static void del_nbp(struct net_bridge_port *p)  	br_ifinfo_notify(RTM_DELLINK, p); +	list_del_rcu(&p->list); +  	nbp_vlan_flush(p);  	br_fdb_delete_by_port(br, p, 1); - -	list_del_rcu(&p->list); +	nbp_update_port_count(br);  	dev->priv_flags &= ~IFF_BRIDGE_PORT; @@ -172,6 +278,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)  		del_nbp(p);  	} +	br_fdb_delete_by_port(br, NULL, 1); + +	br_vlan_flush(br);  	del_timer_sync(&br->gc_timer);  	br_sysfs_delbr(br->dev); @@ -350,7 +459,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)  	call_netdevice_notifiers(NETDEV_JOIN, dev); -	err = dev_set_promiscuity(dev, 1); +	err = dev_set_allmulti(dev, 1);  	if (err)  		goto put_back; @@ -363,7 +472,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)  	if (err)  		goto err2; -	err = br_netpoll_enable(p, GFP_KERNEL); +	err = br_netpoll_enable(p);  	if (err)  		goto err3; @@ -381,11 +490,16 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)  	list_add_rcu(&p->list, &br->port_list); +	nbp_update_port_count(br); +  	netdev_update_features(br->dev);  	if (br->dev->needed_headroom < dev->needed_headroom)  		br->dev->needed_headroom = dev->needed_headroom; +	if (br_fdb_insert(br, p, dev->dev_addr, 0)) +		netdev_err(dev, "failed insert local address bridge forwarding table\n"); +  	spin_lock_bh(&br->lock);  	changed_addr = br_stp_recalculate_bridge_id(br); @@ -401,9 +515,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)  	dev_set_mtu(br->dev, br_min_mtu(br)); -	if (br_fdb_insert(br, p, dev->dev_addr, 0)) -		netdev_err(dev, "failed insert local address bridge forwarding table\n"); -  	kobject_uevent(&p->kobj, KOBJ_ADD);  	return 0; @@ -418,7 +529,7 @@ err2:  	kobject_put(&p->kobj);  	p = NULL; /* kobject_put frees */  err1: -	dev_set_promiscuity(dev, -1); +	dev_set_allmulti(dev, -1);  put_back:  	dev_put(dev);  	kfree(p); @@ -453,17 +564,10 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)  	return 0;  } -void __net_exit br_net_exit(struct net *net) +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)  { -	struct net_device *dev; -	LIST_HEAD(list); - -	rtnl_lock(); -	for_each_netdev(net, dev) -		if (dev->priv_flags & IFF_EBRIDGE) -			br_dev_delete(dev, &list); - -	unregister_netdevice_many(&list); -	rtnl_unlock(); +	struct net_bridge *br = p->br; +	if (mask & BR_AUTO_MASK) +		nbp_update_port_count(br);  } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index a2fd37ec35f..366c4364907 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -28,7 +28,8 @@ static int br_pass_frame_up(struct sk_buff *skb)  {  	struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;  	struct net_bridge *br = netdev_priv(brdev); -	struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); +	struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); +	struct net_port_vlans *pv;  	u64_stats_update_begin(&brstats->syncp);  	brstats->rx_packets++; @@ -39,18 +40,18 @@ static int br_pass_frame_up(struct sk_buff *skb)  	 * packet is allowed except in promisc modue when someone  	 * may be running packet capture.  	 */ +	pv = br_get_vlan_info(br);  	if (!(brdev->flags & IFF_PROMISC) && -	    !br_allowed_egress(br, br_get_vlan_info(br), skb)) { +	    !br_allowed_egress(br, pv, skb)) {  		kfree_skb(skb);  		return NET_RX_DROP;  	} -	skb = br_handle_vlan(br, br_get_vlan_info(br), skb); -	if (!skb) -		return NET_RX_DROP; -  	indev = skb->dev;  	skb->dev = brdev; +	skb = br_handle_vlan(br, pv, skb); +	if (!skb) +		return NET_RX_DROP;  	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,  		       netif_receive_skb); @@ -72,15 +73,15 @@ int br_handle_frame_finish(struct sk_buff *skb)  		goto drop;  	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) -		goto drop; +		goto out;  	/* insert into forwarding database after filtering to avoid spoofing */  	br = p->br;  	if (p->flags & BR_LEARNING) -		br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); +		br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);  	if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && -	    br_multicast_rcv(br, p, skb)) +	    br_multicast_rcv(br, p, skb, vid))  		goto drop;  	if (p->state == BR_STATE_LEARNING) @@ -146,9 +147,9 @@ static int br_handle_local_finish(struct sk_buff *skb)  	struct net_bridge_port *p = br_port_get_rcu(skb->dev);  	u16 vid = 0; -	br_vlan_get_tag(skb, &vid); -	if (p->flags & BR_LEARNING) -		br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid); +	/* check if vlan is allowed, to avoid spoofing */ +	if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) +		br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);  	return 0;	 /* process further */  } @@ -176,6 +177,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)  	p = br_port_get_rcu(skb->dev);  	if (unlikely(is_link_local_ether_addr(dest))) { +		u16 fwd_mask = p->br->group_fwd_mask_required; +  		/*  		 * See IEEE 802.1D Table 7-10 Reserved addresses  		 * @@ -193,7 +196,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)  		case 0x00:	/* Bridge Group Address */  			/* If STP is turned off,  			   then must forward to keep loop detection */ -			if (p->br->stp_enabled == BR_NO_STP) +			if (p->br->stp_enabled == BR_NO_STP || +			    fwd_mask & (1u << dest[5]))  				goto forward;  			break; @@ -202,7 +206,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)  		default:  			/* Allow selective forwarding for most other protocols */ -			if (p->br->group_fwd_mask & (1u << dest[5])) +			fwd_mask |= p->br->group_fwd_mask; +			if (fwd_mask & (1u << dest[5]))  				goto forward;  		} diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index cd8c3a44ab7..a9a4a1b7863 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -381,7 +381,7 @@ int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  {  	struct net_bridge *br = netdev_priv(dev); -	switch(cmd) { +	switch (cmd) {  	case SIOCDEVPRIVATE:  		return old_dev_ioctl(dev, rq, cmd); diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 85a09bb5ca5..5df05269d17 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -418,13 +418,13 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)  	ip.proto = entry->addr.proto;  	if (ip.proto == htons(ETH_P_IP)) { -		if (timer_pending(&br->ip4_querier.timer)) +		if (timer_pending(&br->ip4_other_query.timer))  			return -EBUSY;  		ip.u.ip4 = entry->addr.u.ip4;  #if IS_ENABLED(CONFIG_IPV6)  	} else { -		if (timer_pending(&br->ip6_querier.timer)) +		if (timer_pending(&br->ip6_other_query.timer))  			return -EBUSY;  		ip.u.ip6 = entry->addr.u.ip6; @@ -453,7 +453,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)  		call_rcu_bh(&p->rcu, br_multicast_free_pg);  		err = 0; -		if (!mp->ports && !mp->mglist && mp->timer_armed && +		if (!mp->ports && !mp->mglist &&  		    netif_running(br->dev))  			mod_timer(&mp->timer, jiffies);  		break; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index d1c57863067..abfa0b65a11 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -11,6 +11,7 @@   */  #include <linux/err.h> +#include <linux/export.h>  #include <linux/if_ether.h>  #include <linux/igmp.h>  #include <linux/jhash.h> @@ -35,7 +36,7 @@  #include "br_private.h"  static void br_multicast_start_querier(struct net_bridge *br, -				       struct bridge_mcast_query *query); +				       struct bridge_mcast_own_query *query);  unsigned int br_mdb_rehash_seq;  static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) @@ -272,7 +273,7 @@ static void br_multicast_del_pg(struct net_bridge *br,  		del_timer(&p->timer);  		call_rcu_bh(&p->rcu, br_multicast_free_pg); -		if (!mp->ports && !mp->mglist && mp->timer_armed && +		if (!mp->ports && !mp->mglist &&  		    netif_running(br->dev))  			mod_timer(&mp->timer, jiffies); @@ -363,7 +364,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,  	skb_reset_mac_header(skb);  	eth = eth_hdr(skb); -	memcpy(eth->h_source, br->dev->dev_addr, 6); +	ether_addr_copy(eth->h_source, br->dev->dev_addr);  	eth->h_dest[0] = 1;  	eth->h_dest[1] = 0;  	eth->h_dest[2] = 0x5e; @@ -433,7 +434,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,  	skb_reset_mac_header(skb);  	eth = eth_hdr(skb); -	memcpy(eth->h_source, br->dev->dev_addr, 6); +	ether_addr_copy(eth->h_source, br->dev->dev_addr);  	eth->h_proto = htons(ETH_P_IPV6);  	skb_put(skb, sizeof(*eth)); @@ -620,7 +621,6 @@ rehash:  	mp->br = br;  	mp->addr = *group; -  	setup_timer(&mp->timer, br_multicast_group_expired,  		    (unsigned long)mp); @@ -660,6 +660,7 @@ static int br_multicast_add_group(struct net_bridge *br,  	struct net_bridge_mdb_entry *mp;  	struct net_bridge_port_group *p;  	struct net_bridge_port_group __rcu **pp; +	unsigned long now = jiffies;  	int err;  	spin_lock(&br->multicast_lock); @@ -674,6 +675,7 @@ static int br_multicast_add_group(struct net_bridge *br,  	if (!port) {  		mp->mglist = true; +		mod_timer(&mp->timer, now + br->multicast_membership_interval);  		goto out;  	} @@ -681,7 +683,7 @@ static int br_multicast_add_group(struct net_bridge *br,  	     (p = mlock_dereference(*pp, br)) != NULL;  	     pp = &p->next) {  		if (p->port == port) -			goto out; +			goto found;  		if ((unsigned long)p->port < (unsigned long)port)  			break;  	} @@ -692,6 +694,8 @@ static int br_multicast_add_group(struct net_bridge *br,  	rcu_assign_pointer(*pp, p);  	br_mdb_notify(br->dev, port, group, RTM_NEWMDB); +found: +	mod_timer(&p->timer, now + br->multicast_membership_interval);  out:  	err = 0; @@ -758,7 +762,7 @@ static void br_multicast_local_router_expired(unsigned long data)  }  static void br_multicast_querier_expired(struct net_bridge *br, -					 struct bridge_mcast_query *query) +					 struct bridge_mcast_own_query *query)  {  	spin_lock(&br->multicast_lock);  	if (!netif_running(br->dev) || br->multicast_disabled) @@ -774,7 +778,7 @@ static void br_ip4_multicast_querier_expired(unsigned long data)  {  	struct net_bridge *br = (void *)data; -	br_multicast_querier_expired(br, &br->ip4_query); +	br_multicast_querier_expired(br, &br->ip4_own_query);  }  #if IS_ENABLED(CONFIG_IPV6) @@ -782,10 +786,22 @@ static void br_ip6_multicast_querier_expired(unsigned long data)  {  	struct net_bridge *br = (void *)data; -	br_multicast_querier_expired(br, &br->ip6_query); +	br_multicast_querier_expired(br, &br->ip6_own_query);  }  #endif +static void br_multicast_select_own_querier(struct net_bridge *br, +					    struct br_ip *ip, +					    struct sk_buff *skb) +{ +	if (ip->proto == htons(ETH_P_IP)) +		br->ip4_querier.addr.u.ip4 = ip_hdr(skb)->saddr; +#if IS_ENABLED(CONFIG_IPV6) +	else +		br->ip6_querier.addr.u.ip6 = ipv6_hdr(skb)->saddr; +#endif +} +  static void __br_multicast_send_query(struct net_bridge *br,  				      struct net_bridge_port *port,  				      struct br_ip *ip) @@ -801,17 +817,19 @@ static void __br_multicast_send_query(struct net_bridge *br,  		skb->dev = port->dev;  		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,  			dev_queue_xmit); -	} else +	} else { +		br_multicast_select_own_querier(br, ip, skb);  		netif_rx(skb); +	}  }  static void br_multicast_send_query(struct net_bridge *br,  				    struct net_bridge_port *port, -				    struct bridge_mcast_query *query) +				    struct bridge_mcast_own_query *own_query)  {  	unsigned long time;  	struct br_ip br_group; -	struct bridge_mcast_querier *querier = NULL; +	struct bridge_mcast_other_query *other_query = NULL;  	if (!netif_running(br->dev) || br->multicast_disabled ||  	    !br->multicast_querier) @@ -819,31 +837,32 @@ static void br_multicast_send_query(struct net_bridge *br,  	memset(&br_group.u, 0, sizeof(br_group.u)); -	if (port ? (query == &port->ip4_query) : -		   (query == &br->ip4_query)) { -		querier = &br->ip4_querier; +	if (port ? (own_query == &port->ip4_own_query) : +		   (own_query == &br->ip4_own_query)) { +		other_query = &br->ip4_other_query;  		br_group.proto = htons(ETH_P_IP);  #if IS_ENABLED(CONFIG_IPV6)  	} else { -		querier = &br->ip6_querier; +		other_query = &br->ip6_other_query;  		br_group.proto = htons(ETH_P_IPV6);  #endif  	} -	if (!querier || timer_pending(&querier->timer)) +	if (!other_query || timer_pending(&other_query->timer))  		return;  	__br_multicast_send_query(br, port, &br_group);  	time = jiffies; -	time += query->startup_sent < br->multicast_startup_query_count ? +	time += own_query->startup_sent < br->multicast_startup_query_count ?  		br->multicast_startup_query_interval :  		br->multicast_query_interval; -	mod_timer(&query->timer, time); +	mod_timer(&own_query->timer, time);  } -static void br_multicast_port_query_expired(struct net_bridge_port *port, -					    struct bridge_mcast_query *query) +static void +br_multicast_port_query_expired(struct net_bridge_port *port, +				struct bridge_mcast_own_query *query)  {  	struct net_bridge *br = port->br; @@ -865,7 +884,7 @@ static void br_ip4_multicast_port_query_expired(unsigned long data)  {  	struct net_bridge_port *port = (void *)data; -	br_multicast_port_query_expired(port, &port->ip4_query); +	br_multicast_port_query_expired(port, &port->ip4_own_query);  }  #if IS_ENABLED(CONFIG_IPV6) @@ -873,7 +892,7 @@ static void br_ip6_multicast_port_query_expired(unsigned long data)  {  	struct net_bridge_port *port = (void *)data; -	br_multicast_port_query_expired(port, &port->ip6_query); +	br_multicast_port_query_expired(port, &port->ip6_own_query);  }  #endif @@ -883,11 +902,11 @@ void br_multicast_add_port(struct net_bridge_port *port)  	setup_timer(&port->multicast_router_timer, br_multicast_router_expired,  		    (unsigned long)port); -	setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired, -		    (unsigned long)port); +	setup_timer(&port->ip4_own_query.timer, +		    br_ip4_multicast_port_query_expired, (unsigned long)port);  #if IS_ENABLED(CONFIG_IPV6) -	setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired, -		    (unsigned long)port); +	setup_timer(&port->ip6_own_query.timer, +		    br_ip6_multicast_port_query_expired, (unsigned long)port);  #endif  } @@ -896,7 +915,7 @@ void br_multicast_del_port(struct net_bridge_port *port)  	del_timer_sync(&port->multicast_router_timer);  } -static void br_multicast_enable(struct bridge_mcast_query *query) +static void br_multicast_enable(struct bridge_mcast_own_query *query)  {  	query->startup_sent = 0; @@ -913,9 +932,9 @@ void br_multicast_enable_port(struct net_bridge_port *port)  	if (br->multicast_disabled || !netif_running(br->dev))  		goto out; -	br_multicast_enable(&port->ip4_query); +	br_multicast_enable(&port->ip4_own_query);  #if IS_ENABLED(CONFIG_IPV6) -	br_multicast_enable(&port->ip6_query); +	br_multicast_enable(&port->ip6_own_query);  #endif  out: @@ -935,16 +954,17 @@ void br_multicast_disable_port(struct net_bridge_port *port)  	if (!hlist_unhashed(&port->rlist))  		hlist_del_init_rcu(&port->rlist);  	del_timer(&port->multicast_router_timer); -	del_timer(&port->ip4_query.timer); +	del_timer(&port->ip4_own_query.timer);  #if IS_ENABLED(CONFIG_IPV6) -	del_timer(&port->ip6_query.timer); +	del_timer(&port->ip6_own_query.timer);  #endif  	spin_unlock(&br->multicast_lock);  }  static int br_ip4_multicast_igmp3_report(struct net_bridge *br,  					 struct net_bridge_port *port, -					 struct sk_buff *skb) +					 struct sk_buff *skb, +					 u16 vid)  {  	struct igmpv3_report *ih;  	struct igmpv3_grec *grec; @@ -954,12 +974,10 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,  	int type;  	int err = 0;  	__be32 group; -	u16 vid = 0;  	if (!pskb_may_pull(skb, sizeof(*ih)))  		return -EINVAL; -	br_vlan_get_tag(skb, &vid);  	ih = igmpv3_report_hdr(skb);  	num = ntohs(ih->ngrec);  	len = sizeof(*ih); @@ -1002,7 +1020,8 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,  #if IS_ENABLED(CONFIG_IPV6)  static int br_ip6_multicast_mld2_report(struct net_bridge *br,  					struct net_bridge_port *port, -					struct sk_buff *skb) +					struct sk_buff *skb, +					u16 vid)  {  	struct icmp6hdr *icmp6h;  	struct mld2_grec *grec; @@ -1010,12 +1029,10 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,  	int len;  	int num;  	int err = 0; -	u16 vid = 0;  	if (!pskb_may_pull(skb, sizeof(*icmp6h)))  		return -EINVAL; -	br_vlan_get_tag(skb, &vid);  	icmp6h = icmp6_hdr(skb);  	num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);  	len = sizeof(*icmp6h); @@ -1063,15 +1080,80 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,  }  #endif +static bool br_ip4_multicast_select_querier(struct net_bridge *br, +					    struct net_bridge_port *port, +					    __be32 saddr) +{ +	if (!timer_pending(&br->ip4_own_query.timer) && +	    !timer_pending(&br->ip4_other_query.timer)) +		goto update; + +	if (!br->ip4_querier.addr.u.ip4) +		goto update; + +	if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.u.ip4)) +		goto update; + +	return false; + +update: +	br->ip4_querier.addr.u.ip4 = saddr; + +	/* update protected by general multicast_lock by caller */ +	rcu_assign_pointer(br->ip4_querier.port, port); + +	return true; +} + +#if IS_ENABLED(CONFIG_IPV6) +static bool br_ip6_multicast_select_querier(struct net_bridge *br, +					    struct net_bridge_port *port, +					    struct in6_addr *saddr) +{ +	if (!timer_pending(&br->ip6_own_query.timer) && +	    !timer_pending(&br->ip6_other_query.timer)) +		goto update; + +	if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.u.ip6) <= 0) +		goto update; + +	return false; + +update: +	br->ip6_querier.addr.u.ip6 = *saddr; + +	/* update protected by general multicast_lock by caller */ +	rcu_assign_pointer(br->ip6_querier.port, port); + +	return true; +} +#endif + +static bool br_multicast_select_querier(struct net_bridge *br, +					struct net_bridge_port *port, +					struct br_ip *saddr) +{ +	switch (saddr->proto) { +	case htons(ETH_P_IP): +		return br_ip4_multicast_select_querier(br, port, saddr->u.ip4); +#if IS_ENABLED(CONFIG_IPV6) +	case htons(ETH_P_IPV6): +		return br_ip6_multicast_select_querier(br, port, &saddr->u.ip6); +#endif +	} + +	return false; +} +  static void -br_multicast_update_querier_timer(struct net_bridge *br, -				  struct bridge_mcast_querier *querier, -				  unsigned long max_delay) +br_multicast_update_query_timer(struct net_bridge *br, +				struct bridge_mcast_other_query *query, +				unsigned long max_delay)  { -	if (!timer_pending(&querier->timer)) -		querier->delay_time = jiffies + max_delay; +	if (!timer_pending(&query->timer)) +		query->delay_time = jiffies + max_delay; -	mod_timer(&querier->timer, jiffies + br->multicast_querier_interval); +	mod_timer(&query->timer, jiffies + br->multicast_querier_interval);  }  /* @@ -1124,21 +1206,21 @@ timer:  static void br_multicast_query_received(struct net_bridge *br,  					struct net_bridge_port *port, -					struct bridge_mcast_querier *querier, -					int saddr, +					struct bridge_mcast_other_query *query, +					struct br_ip *saddr,  					unsigned long max_delay)  { -	if (saddr) -		br_multicast_update_querier_timer(br, querier, max_delay); -	else if (timer_pending(&querier->timer)) +	if (!br_multicast_select_querier(br, port, saddr))  		return; +	br_multicast_update_query_timer(br, query, max_delay);  	br_multicast_mark_router(br, port);  }  static int br_ip4_multicast_query(struct net_bridge *br,  				  struct net_bridge_port *port, -				  struct sk_buff *skb) +				  struct sk_buff *skb, +				  u16 vid)  {  	const struct iphdr *iph = ip_hdr(skb);  	struct igmphdr *ih = igmp_hdr(skb); @@ -1146,11 +1228,11 @@ static int br_ip4_multicast_query(struct net_bridge *br,  	struct igmpv3_query *ih3;  	struct net_bridge_port_group *p;  	struct net_bridge_port_group __rcu **pp; +	struct br_ip saddr;  	unsigned long max_delay;  	unsigned long now = jiffies;  	__be32 group;  	int err = 0; -	u16 vid = 0;  	spin_lock(&br->multicast_lock);  	if (!netif_running(br->dev) || @@ -1180,20 +1262,27 @@ static int br_ip4_multicast_query(struct net_bridge *br,  			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;  	} -	br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr, -				    max_delay); +	/* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer +	 * all-systems destination addresses (224.0.0.1) for general queries +	 */ +	if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) { +		err = -EINVAL; +		goto out; +	} + +	if (!group) { +		saddr.proto = htons(ETH_P_IP); +		saddr.u.ip4 = iph->saddr; -	if (!group) +		br_multicast_query_received(br, port, &br->ip4_other_query, +					    &saddr, max_delay);  		goto out; +	} -	br_vlan_get_tag(skb, &vid);  	mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);  	if (!mp)  		goto out; -	mod_timer(&mp->timer, now + br->multicast_membership_interval); -	mp->timer_armed = true; -  	max_delay *= br->multicast_last_member_count;  	if (mp->mglist && @@ -1219,7 +1308,8 @@ out:  #if IS_ENABLED(CONFIG_IPV6)  static int br_ip6_multicast_query(struct net_bridge *br,  				  struct net_bridge_port *port, -				  struct sk_buff *skb) +				  struct sk_buff *skb, +				  u16 vid)  {  	const struct ipv6hdr *ip6h = ipv6_hdr(skb);  	struct mld_msg *mld; @@ -1227,17 +1317,24 @@ static int br_ip6_multicast_query(struct net_bridge *br,  	struct mld2_query *mld2q;  	struct net_bridge_port_group *p;  	struct net_bridge_port_group __rcu **pp; +	struct br_ip saddr;  	unsigned long max_delay;  	unsigned long now = jiffies;  	const struct in6_addr *group = NULL; +	bool is_general_query;  	int err = 0; -	u16 vid = 0;  	spin_lock(&br->multicast_lock);  	if (!netif_running(br->dev) ||  	    (port && port->state == BR_STATE_DISABLED))  		goto out; +	/* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ +	if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) { +		err = -EINVAL; +		goto out; +	} +  	if (skb->len == sizeof(*mld)) {  		if (!pskb_may_pull(skb, sizeof(*mld))) {  			err = -EINVAL; @@ -1259,20 +1356,31 @@ static int br_ip6_multicast_query(struct net_bridge *br,  		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);  	} -	br_multicast_query_received(br, port, &br->ip6_querier, -				    !ipv6_addr_any(&ip6h->saddr), max_delay); +	is_general_query = group && ipv6_addr_any(group); -	if (!group) +	/* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer +	 * all-nodes destination address (ff02::1) for general queries +	 */ +	if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) { +		err = -EINVAL;  		goto out; +	} + +	if (is_general_query) { +		saddr.proto = htons(ETH_P_IPV6); +		saddr.u.ip6 = ip6h->saddr; + +		br_multicast_query_received(br, port, &br->ip6_other_query, +					    &saddr, max_delay); +		goto out; +	} else if (!group) { +		goto out; +	} -	br_vlan_get_tag(skb, &vid);  	mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);  	if (!mp)  		goto out; -	mod_timer(&mp->timer, now + br->multicast_membership_interval); -	mp->timer_armed = true; -  	max_delay *= br->multicast_last_member_count;  	if (mp->mglist &&  	    (timer_pending(&mp->timer) ? @@ -1295,11 +1403,12 @@ out:  }  #endif -static void br_multicast_leave_group(struct net_bridge *br, -				     struct net_bridge_port *port, -				     struct br_ip *group, -				     struct bridge_mcast_querier *querier, -				     struct bridge_mcast_query *query) +static void +br_multicast_leave_group(struct net_bridge *br, +			 struct net_bridge_port *port, +			 struct br_ip *group, +			 struct bridge_mcast_other_query *other_query, +			 struct bridge_mcast_own_query *own_query)  {  	struct net_bridge_mdb_htable *mdb;  	struct net_bridge_mdb_entry *mp; @@ -1310,7 +1419,7 @@ static void br_multicast_leave_group(struct net_bridge *br,  	spin_lock(&br->multicast_lock);  	if (!netif_running(br->dev) ||  	    (port && port->state == BR_STATE_DISABLED) || -	    timer_pending(&querier->timer)) +	    timer_pending(&other_query->timer))  		goto out;  	mdb = mlock_dereference(br->mdb, br); @@ -1324,7 +1433,7 @@ static void br_multicast_leave_group(struct net_bridge *br,  		time = jiffies + br->multicast_last_member_count *  				 br->multicast_last_member_interval; -		mod_timer(&query->timer, time); +		mod_timer(&own_query->timer, time);  		for (p = mlock_dereference(mp->ports, br);  		     p != NULL; @@ -1358,7 +1467,7 @@ static void br_multicast_leave_group(struct net_bridge *br,  			call_rcu_bh(&p->rcu, br_multicast_free_pg);  			br_mdb_notify(br->dev, port, group, RTM_DELMDB); -			if (!mp->ports && !mp->mglist && mp->timer_armed && +			if (!mp->ports && !mp->mglist &&  			    netif_running(br->dev))  				mod_timer(&mp->timer, jiffies);  		} @@ -1370,12 +1479,30 @@ static void br_multicast_leave_group(struct net_bridge *br,  		     br->multicast_last_member_interval;  	if (!port) { -		if (mp->mglist && mp->timer_armed && +		if (mp->mglist &&  		    (timer_pending(&mp->timer) ?  		     time_after(mp->timer.expires, time) :  		     try_to_del_timer_sync(&mp->timer) >= 0)) {  			mod_timer(&mp->timer, time);  		} + +		goto out; +	} + +	for (p = mlock_dereference(mp->ports, br); +	     p != NULL; +	     p = mlock_dereference(p->next, br)) { +		if (p->port != port) +			continue; + +		if (!hlist_unhashed(&p->mglist) && +		    (timer_pending(&p->timer) ? +		     time_after(p->timer.expires, time) : +		     try_to_del_timer_sync(&p->timer) >= 0)) { +			mod_timer(&p->timer, time); +		} + +		break;  	}  out:  	spin_unlock(&br->multicast_lock); @@ -1387,17 +1514,19 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,  					 __u16 vid)  {  	struct br_ip br_group; -	struct bridge_mcast_query *query = port ? &port->ip4_query : -						  &br->ip4_query; +	struct bridge_mcast_own_query *own_query;  	if (ipv4_is_local_multicast(group))  		return; +	own_query = port ? &port->ip4_own_query : &br->ip4_own_query; +  	br_group.u.ip4 = group;  	br_group.proto = htons(ETH_P_IP);  	br_group.vid = vid; -	br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query); +	br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, +				 own_query);  }  #if IS_ENABLED(CONFIG_IPV6) @@ -1407,24 +1536,26 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,  					 __u16 vid)  {  	struct br_ip br_group; -	struct bridge_mcast_query *query = port ? &port->ip6_query : -						  &br->ip6_query; - +	struct bridge_mcast_own_query *own_query;  	if (ipv6_addr_is_ll_all_nodes(group))  		return; +	own_query = port ? &port->ip6_own_query : &br->ip6_own_query; +  	br_group.u.ip6 = *group;  	br_group.proto = htons(ETH_P_IPV6);  	br_group.vid = vid; -	br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query); +	br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, +				 own_query);  }  #endif  static int br_multicast_ipv4_rcv(struct net_bridge *br,  				 struct net_bridge_port *port, -				 struct sk_buff *skb) +				 struct sk_buff *skb, +				 u16 vid)  {  	struct sk_buff *skb2 = skb;  	const struct iphdr *iph; @@ -1432,7 +1563,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,  	unsigned int len;  	unsigned int offset;  	int err; -	u16 vid = 0;  	/* We treat OOM as packet loss for now. */  	if (!pskb_may_pull(skb, sizeof(*iph))) @@ -1493,7 +1623,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,  	err = 0; -	br_vlan_get_tag(skb2, &vid);  	BR_INPUT_SKB_CB(skb)->igmp = 1;  	ih = igmp_hdr(skb2); @@ -1504,10 +1633,10 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,  		err = br_ip4_multicast_add_group(br, port, ih->group, vid);  		break;  	case IGMPV3_HOST_MEMBERSHIP_REPORT: -		err = br_ip4_multicast_igmp3_report(br, port, skb2); +		err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);  		break;  	case IGMP_HOST_MEMBERSHIP_QUERY: -		err = br_ip4_multicast_query(br, port, skb2); +		err = br_ip4_multicast_query(br, port, skb2, vid);  		break;  	case IGMP_HOST_LEAVE_MESSAGE:  		br_ip4_multicast_leave_group(br, port, ih->group, vid); @@ -1525,7 +1654,8 @@ err_out:  #if IS_ENABLED(CONFIG_IPV6)  static int br_multicast_ipv6_rcv(struct net_bridge *br,  				 struct net_bridge_port *port, -				 struct sk_buff *skb) +				 struct sk_buff *skb, +				 u16 vid)  {  	struct sk_buff *skb2;  	const struct ipv6hdr *ip6h; @@ -1535,7 +1665,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,  	unsigned int len;  	int offset;  	int err; -	u16 vid = 0;  	if (!pskb_may_pull(skb, sizeof(*ip6h)))  		return -EINVAL; @@ -1625,7 +1754,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,  	err = 0; -	br_vlan_get_tag(skb, &vid);  	BR_INPUT_SKB_CB(skb)->igmp = 1;  	switch (icmp6_type) { @@ -1642,10 +1770,10 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,  		break;  	    }  	case ICMPV6_MLD2_REPORT: -		err = br_ip6_multicast_mld2_report(br, port, skb2); +		err = br_ip6_multicast_mld2_report(br, port, skb2, vid);  		break;  	case ICMPV6_MGM_QUERY: -		err = br_ip6_multicast_query(br, port, skb2); +		err = br_ip6_multicast_query(br, port, skb2, vid);  		break;  	case ICMPV6_MGM_REDUCTION:  	    { @@ -1666,7 +1794,7 @@ out:  #endif  int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, -		     struct sk_buff *skb) +		     struct sk_buff *skb, u16 vid)  {  	BR_INPUT_SKB_CB(skb)->igmp = 0;  	BR_INPUT_SKB_CB(skb)->mrouters_only = 0; @@ -1676,10 +1804,10 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,  	switch (skb->protocol) {  	case htons(ETH_P_IP): -		return br_multicast_ipv4_rcv(br, port, skb); +		return br_multicast_ipv4_rcv(br, port, skb, vid);  #if IS_ENABLED(CONFIG_IPV6)  	case htons(ETH_P_IPV6): -		return br_multicast_ipv6_rcv(br, port, skb); +		return br_multicast_ipv6_rcv(br, port, skb, vid);  #endif  	} @@ -1687,12 +1815,14 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,  }  static void br_multicast_query_expired(struct net_bridge *br, -				       struct bridge_mcast_query *query) +				       struct bridge_mcast_own_query *query, +				       struct bridge_mcast_querier *querier)  {  	spin_lock(&br->multicast_lock);  	if (query->startup_sent < br->multicast_startup_query_count)  		query->startup_sent++; +	rcu_assign_pointer(querier, NULL);  	br_multicast_send_query(br, NULL, query);  	spin_unlock(&br->multicast_lock);  } @@ -1701,7 +1831,7 @@ static void br_ip4_multicast_query_expired(unsigned long data)  {  	struct net_bridge *br = (void *)data; -	br_multicast_query_expired(br, &br->ip4_query); +	br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);  }  #if IS_ENABLED(CONFIG_IPV6) @@ -1709,7 +1839,7 @@ static void br_ip6_multicast_query_expired(unsigned long data)  {  	struct net_bridge *br = (void *)data; -	br_multicast_query_expired(br, &br->ip6_query); +	br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier);  }  #endif @@ -1731,28 +1861,30 @@ void br_multicast_init(struct net_bridge *br)  	br->multicast_querier_interval = 255 * HZ;  	br->multicast_membership_interval = 260 * HZ; -	br->ip4_querier.delay_time = 0; +	br->ip4_other_query.delay_time = 0; +	br->ip4_querier.port = NULL;  #if IS_ENABLED(CONFIG_IPV6) -	br->ip6_querier.delay_time = 0; +	br->ip6_other_query.delay_time = 0; +	br->ip6_querier.port = NULL;  #endif  	spin_lock_init(&br->multicast_lock);  	setup_timer(&br->multicast_router_timer,  		    br_multicast_local_router_expired, 0); -	setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired, -		    (unsigned long)br); -	setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired, +	setup_timer(&br->ip4_other_query.timer, +		    br_ip4_multicast_querier_expired, (unsigned long)br); +	setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired,  		    (unsigned long)br);  #if IS_ENABLED(CONFIG_IPV6) -	setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired, -		    (unsigned long)br); -	setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired, +	setup_timer(&br->ip6_other_query.timer, +		    br_ip6_multicast_querier_expired, (unsigned long)br); +	setup_timer(&br->ip6_own_query.timer, br_ip6_multicast_query_expired,  		    (unsigned long)br);  #endif  }  static void __br_multicast_open(struct net_bridge *br, -				struct bridge_mcast_query *query) +				struct bridge_mcast_own_query *query)  {  	query->startup_sent = 0; @@ -1764,9 +1896,9 @@ static void __br_multicast_open(struct net_bridge *br,  void br_multicast_open(struct net_bridge *br)  { -	__br_multicast_open(br, &br->ip4_query); +	__br_multicast_open(br, &br->ip4_own_query);  #if IS_ENABLED(CONFIG_IPV6) -	__br_multicast_open(br, &br->ip6_query); +	__br_multicast_open(br, &br->ip6_own_query);  #endif  } @@ -1779,11 +1911,11 @@ void br_multicast_stop(struct net_bridge *br)  	int i;  	del_timer_sync(&br->multicast_router_timer); -	del_timer_sync(&br->ip4_querier.timer); -	del_timer_sync(&br->ip4_query.timer); +	del_timer_sync(&br->ip4_other_query.timer); +	del_timer_sync(&br->ip4_own_query.timer);  #if IS_ENABLED(CONFIG_IPV6) -	del_timer_sync(&br->ip6_querier.timer); -	del_timer_sync(&br->ip6_query.timer); +	del_timer_sync(&br->ip6_other_query.timer); +	del_timer_sync(&br->ip6_own_query.timer);  #endif  	spin_lock_bh(&br->multicast_lock); @@ -1798,7 +1930,6 @@ void br_multicast_stop(struct net_bridge *br)  		hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],  					  hlist[ver]) {  			del_timer(&mp->timer); -			mp->timer_armed = false;  			call_rcu_bh(&mp->rcu, br_multicast_free_group);  		}  	} @@ -1888,7 +2019,7 @@ unlock:  }  static void br_multicast_start_querier(struct net_bridge *br, -				       struct bridge_mcast_query *query) +				       struct bridge_mcast_own_query *query)  {  	struct net_bridge_port *port; @@ -1899,11 +2030,11 @@ static void br_multicast_start_querier(struct net_bridge *br,  		    port->state == BR_STATE_BLOCKING)  			continue; -		if (query == &br->ip4_query) -			br_multicast_enable(&port->ip4_query); +		if (query == &br->ip4_own_query) +			br_multicast_enable(&port->ip4_own_query);  #if IS_ENABLED(CONFIG_IPV6)  		else -			br_multicast_enable(&port->ip6_query); +			br_multicast_enable(&port->ip6_own_query);  #endif  	}  } @@ -1939,9 +2070,9 @@ rollback:  			goto rollback;  	} -	br_multicast_start_querier(br, &br->ip4_query); +	br_multicast_start_querier(br, &br->ip4_own_query);  #if IS_ENABLED(CONFIG_IPV6) -	br_multicast_start_querier(br, &br->ip6_query); +	br_multicast_start_querier(br, &br->ip6_own_query);  #endif  unlock: @@ -1966,16 +2097,16 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)  	max_delay = br->multicast_query_response_interval; -	if (!timer_pending(&br->ip4_querier.timer)) -		br->ip4_querier.delay_time = jiffies + max_delay; +	if (!timer_pending(&br->ip4_other_query.timer)) +		br->ip4_other_query.delay_time = jiffies + max_delay; -	br_multicast_start_querier(br, &br->ip4_query); +	br_multicast_start_querier(br, &br->ip4_own_query);  #if IS_ENABLED(CONFIG_IPV6) -	if (!timer_pending(&br->ip6_querier.timer)) -		br->ip6_querier.delay_time = jiffies + max_delay; +	if (!timer_pending(&br->ip6_other_query.timer)) +		br->ip6_other_query.delay_time = jiffies + max_delay; -	br_multicast_start_querier(br, &br->ip6_query); +	br_multicast_start_querier(br, &br->ip6_own_query);  #endif  unlock: @@ -1990,7 +2121,7 @@ int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)  	u32 old;  	struct net_bridge_mdb_htable *mdb; -	spin_lock(&br->multicast_lock); +	spin_lock_bh(&br->multicast_lock);  	if (!netif_running(br->dev))  		goto unlock; @@ -2022,7 +2153,113 @@ rollback:  	}  unlock: -	spin_unlock(&br->multicast_lock); +	spin_unlock_bh(&br->multicast_lock);  	return err;  } + +/** + * br_multicast_list_adjacent - Returns snooped multicast addresses + * @dev:	The bridge port adjacent to which to retrieve addresses + * @br_ip_list:	The list to store found, snooped multicast IP addresses in + * + * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast + * snooping feature on all bridge ports of dev's bridge device, excluding + * the addresses from dev itself. + * + * Returns the number of items added to br_ip_list. + * + * Notes: + * - br_ip_list needs to be initialized by caller + * - br_ip_list might contain duplicates in the end + *   (needs to be taken care of by caller) + * - br_ip_list needs to be freed by caller + */ +int br_multicast_list_adjacent(struct net_device *dev, +			       struct list_head *br_ip_list) +{ +	struct net_bridge *br; +	struct net_bridge_port *port; +	struct net_bridge_port_group *group; +	struct br_ip_list *entry; +	int count = 0; + +	rcu_read_lock(); +	if (!br_ip_list || !br_port_exists(dev)) +		goto unlock; + +	port = br_port_get_rcu(dev); +	if (!port || !port->br) +		goto unlock; + +	br = port->br; + +	list_for_each_entry_rcu(port, &br->port_list, list) { +		if (!port->dev || port->dev == dev) +			continue; + +		hlist_for_each_entry_rcu(group, &port->mglist, mglist) { +			entry = kmalloc(sizeof(*entry), GFP_ATOMIC); +			if (!entry) +				goto unlock; + +			entry->addr = group->addr; +			list_add(&entry->list, br_ip_list); +			count++; +		} +	} + +unlock: +	rcu_read_unlock(); +	return count; +} +EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); + +/** + * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port + * @dev: The bridge port adjacent to which to check for a querier + * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 + * + * Checks whether the given interface has a bridge on top and if so returns + * true if a selected querier is behind one of the other ports of this + * bridge. Otherwise returns false. + */ +bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) +{ +	struct net_bridge *br; +	struct net_bridge_port *port; +	bool ret = false; + +	rcu_read_lock(); +	if (!br_port_exists(dev)) +		goto unlock; + +	port = br_port_get_rcu(dev); +	if (!port || !port->br) +		goto unlock; + +	br = port->br; + +	switch (proto) { +	case ETH_P_IP: +		if (!timer_pending(&br->ip4_other_query.timer) || +		    rcu_dereference(br->ip4_querier.port) == port) +			goto unlock; +		break; +#if IS_ENABLED(CONFIG_IPV6) +	case ETH_P_IPV6: +		if (!timer_pending(&br->ip6_other_query.timer) || +		    rcu_dereference(br->ip6_querier.port) == port) +			goto unlock; +		break; +#endif +	default: +		goto unlock; +	} + +	ret = true; +unlock: +	rcu_read_unlock(); +	return ret; +} +EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index f87736270ea..a615264cf01 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -167,7 +167,7 @@ void br_netfilter_rtable_init(struct net_bridge *br)  	rt->dst.dev = br->dev;  	rt->dst.path = &rt->dst;  	dst_init_metrics(&rt->dst, br_dst_default_metrics, true); -	rt->dst.flags	= DST_NOXFRM | DST_NOPEER | DST_FAKE_RTABLE; +	rt->dst.flags	= DST_NOXFRM | DST_FAKE_RTABLE;  	rt->dst.ops = &fake_dst_ops;  } @@ -506,7 +506,7 @@ bridged_dnat:  					       1);  				return 0;  			} -			memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, ETH_ALEN); +			ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);  			skb->pkt_type = PACKET_HOST;  		}  	} else { @@ -535,7 +535,7 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct  	if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb))  		return br; -	vlan = __vlan_find_dev_deep(br, skb->vlan_proto, +	vlan = __vlan_find_dev_deep_rcu(br, skb->vlan_proto,  				    vlan_tx_tag_get(skb) & VLAN_VID_MASK);  	return vlan ? vlan : br; @@ -559,6 +559,8 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb)  	else if (skb->protocol == htons(ETH_P_PPP_SES))  		nf_bridge->mask |= BRNF_PPPoE; +	/* Must drop socket now because of tproxy. */ +	skb_orphan(skb);  	return skb->dev;  } @@ -619,7 +621,7 @@ bad:  /* Replicate the checks that IPv6 does on packet reception and pass the packet   * to ip6tables, which doesn't support NAT, so things are fairly simple. */ -static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, +static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,  					   struct sk_buff *skb,  					   const struct net_device *in,  					   const struct net_device *out, @@ -669,7 +671,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,   * receiving device) to make netfilter happy, the REDIRECT   * target in particular.  Save the original destination IP   * address to be able to detect DNAT afterwards. */ -static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, +				      struct sk_buff *skb,  				      const struct net_device *in,  				      const struct net_device *out,  				      int (*okfn)(struct sk_buff *)) @@ -691,7 +694,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,  			return NF_ACCEPT;  		nf_bridge_pull_encap_header_rcsum(skb); -		return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); +		return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn);  	}  	if (!brnf_call_iptables && !br->nf_call_iptables) @@ -727,7 +730,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,   * took place when the packet entered the bridge), but we   * register an IPv4 PRE_ROUTING 'sabotage' hook that will   * prevent this from happening. */ -static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_local_in(const struct nf_hook_ops *ops, +				   struct sk_buff *skb,  				   const struct net_device *in,  				   const struct net_device *out,  				   int (*okfn)(struct sk_buff *)) @@ -765,7 +769,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)   * but we are still able to filter on the 'real' indev/outdev   * because of the physdev module. For ARP, indev and outdev are the   * bridge ports. */ -static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, +				     struct sk_buff *skb,  				     const struct net_device *in,  				     const struct net_device *out,  				     int (*okfn)(struct sk_buff *)) @@ -818,7 +823,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,  	return NF_STOLEN;  } -static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, +				      struct sk_buff *skb,  				      const struct net_device *in,  				      const struct net_device *out,  				      int (*okfn)(struct sk_buff *)) @@ -853,12 +859,12 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,  	return NF_STOLEN;  } -#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV4) +#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)  static int br_nf_dev_queue_xmit(struct sk_buff *skb)  {  	int ret; -	if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) && +	if (skb->protocol == htons(ETH_P_IP) &&  	    skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&  	    !skb_is_gso(skb)) {  		if (br_parse_ip_options(skb)) @@ -878,7 +884,8 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb)  #endif  /* PF_BRIDGE/POST_ROUTING ********************************************/ -static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, +				       struct sk_buff *skb,  				       const struct net_device *in,  				       const struct net_device *out,  				       int (*okfn)(struct sk_buff *)) @@ -923,7 +930,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,  /* IP/SABOTAGE *****************************************************/  /* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING   * for the second time. */ -static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb, +static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops, +				   struct sk_buff *skb,  				   const struct net_device *in,  				   const struct net_device *out,  				   int (*okfn)(struct sk_buff *)) @@ -993,7 +1001,7 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {  #ifdef CONFIG_SYSCTL  static  int brnf_sysctl_call_tables(struct ctl_table *ctl, int write, -			    void __user * buffer, size_t * lenp, loff_t * ppos) +			    void __user *buffer, size_t *lenp, loff_t *ppos)  {  	int ret; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index b9259efa636..26edb518b83 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -195,8 +195,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)  	rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);  	return;  errout: -	if (err < 0) -		rtnl_set_sk_err(net, RTNLGRP_LINK, err); +	rtnl_set_sk_err(net, RTNLGRP_LINK, err);  } @@ -207,7 +206,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,  	       struct net_device *dev, u32 filter_mask)  {  	int err = 0; -	struct net_bridge_port *port = br_port_get_rcu(dev); +	struct net_bridge_port *port = br_port_get_rtnl(dev);  	/* not a bridge port and  */  	if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN)) @@ -243,7 +242,7 @@ static int br_afspec(struct net_bridge *br,  		vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); -		if (vinfo->vid >= VLAN_N_VID) +		if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)  			return -EINVAL;  		switch (cmd) { @@ -329,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],  static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])  {  	int err; +	unsigned long old_flags = p->flags;  	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);  	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); @@ -354,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])  		if (err)  			return err;  	} + +	br_port_flags_change(p, old_flags ^ p->flags);  	return 0;  } @@ -373,7 +375,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)  	p = br_port_get_rtnl(dev);  	/* We want to accept dev as bridge itself if the AF_SPEC -	 * is set to see if someone is setting vlan info on the brigde +	 * is set to see if someone is setting vlan info on the bridge  	 */  	if (!p && !afspec)  		return -EINVAL; @@ -389,7 +391,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)  			err = br_setport(p, tb);  			spin_unlock_bh(&p->br->lock);  		} else { -			/* Binary compatability with old RSTP */ +			/* Binary compatibility with old RSTP */  			if (nla_len(protinfo) < sizeof(u8))  				return -EINVAL; @@ -446,12 +448,26 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[])  	return 0;  } +static int br_dev_newlink(struct net *src_net, struct net_device *dev, +			  struct nlattr *tb[], struct nlattr *data[]) +{ +	struct net_bridge *br = netdev_priv(dev); + +	if (tb[IFLA_ADDRESS]) { +		spin_lock_bh(&br->lock); +		br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS])); +		spin_unlock_bh(&br->lock); +	} + +	return register_netdevice(dev); +} +  static size_t br_get_link_af_size(const struct net_device *dev)  {  	struct net_port_vlans *pv;  	if (br_port_exists(dev)) -		pv = nbp_get_vlan_info(br_port_get_rcu(dev)); +		pv = nbp_get_vlan_info(br_port_get_rtnl(dev));  	else if (dev->priv_flags & IFF_EBRIDGE)  		pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));  	else @@ -474,6 +490,7 @@ struct rtnl_link_ops br_link_ops __read_mostly = {  	.priv_size	= sizeof(struct net_bridge),  	.setup		= br_dev_setup,  	.validate	= br_validate, +	.newlink	= br_dev_newlink,  	.dellink	= br_dev_delete,  }; @@ -482,9 +499,7 @@ int __init br_netlink_init(void)  	int err;  	br_mdb_init(); -	err = rtnl_af_register(&br_af_ops); -	if (err) -		goto out; +	rtnl_af_register(&br_af_ops);  	err = rtnl_link_register(&br_link_ops);  	if (err) @@ -494,7 +509,6 @@ int __init br_netlink_init(void)  out_af:  	rtnl_af_unregister(&br_af_ops); -out:  	br_mdb_uninit();  	return err;  } diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c deleted file mode 100644 index 2998dd1769a..00000000000 --- a/net/bridge/br_notify.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - *	Device event handling - *	Linux ethernet bridge - * - *	Authors: - *	Lennert Buytenhek		<buytenh@gnu.org> - * - *	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/kernel.h> -#include <linux/rtnetlink.h> -#include <net/net_namespace.h> - -#include "br_private.h" - -static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr); - -struct notifier_block br_device_notifier = { -	.notifier_call = br_device_event -}; - -/* - * Handle changes in state of network devices enslaved to a bridge. - * - * Note: don't care about up/down if bridge itself is down, because - *     port state is checked when bridge is brought up. - */ -static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) -{ -	struct net_device *dev = netdev_notifier_info_to_dev(ptr); -	struct net_bridge_port *p; -	struct net_bridge *br; -	bool changed_addr; -	int err; - -	/* register of bridge completed, add sysfs entries */ -	if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { -		br_sysfs_addbr(dev); -		return NOTIFY_DONE; -	} - -	/* not a port of a bridge */ -	p = br_port_get_rtnl(dev); -	if (!p) -		return NOTIFY_DONE; - -	br = p->br; - -	switch (event) { -	case NETDEV_CHANGEMTU: -		dev_set_mtu(br->dev, br_min_mtu(br)); -		break; - -	case NETDEV_CHANGEADDR: -		spin_lock_bh(&br->lock); -		br_fdb_changeaddr(p, dev->dev_addr); -		changed_addr = br_stp_recalculate_bridge_id(br); -		spin_unlock_bh(&br->lock); - -		if (changed_addr) -			call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); - -		break; - -	case NETDEV_CHANGE: -		br_port_carrier_check(p); -		break; - -	case NETDEV_FEAT_CHANGE: -		netdev_update_features(br->dev); -		break; - -	case NETDEV_DOWN: -		spin_lock_bh(&br->lock); -		if (br->dev->flags & IFF_UP) -			br_stp_disable_port(p); -		spin_unlock_bh(&br->lock); -		break; - -	case NETDEV_UP: -		if (netif_running(br->dev) && netif_oper_up(dev)) { -			spin_lock_bh(&br->lock); -			br_stp_enable_port(p); -			spin_unlock_bh(&br->lock); -		} -		break; - -	case NETDEV_UNREGISTER: -		br_del_if(br, dev); -		break; - -	case NETDEV_CHANGENAME: -		err = br_sysfs_renameif(p); -		if (err) -			return notifier_from_errno(err); -		break; - -	case NETDEV_PRE_TYPE_CHANGE: -		/* Forbid underlaying device to change its type. */ -		return NOTIFY_BAD; - -	case NETDEV_RESEND_IGMP: -		/* Propagate to master device */ -		call_netdevice_notifiers(event, br->dev); -		break; -	} - -	/* Events that may cause spanning tree to refresh */ -	if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || -	    event == NETDEV_CHANGE || event == NETDEV_DOWN) -		br_ifinfo_notify(RTM_NEWLINK, p); - -	return NOTIFY_DONE; -} diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 598cb0b333c..23caf5b0309 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -35,6 +35,8 @@  #define BR_GROUPFWD_DEFAULT	0  /* Don't allow forwarding control protocols like STP and LLDP */  #define BR_GROUPFWD_RESTRICTED	0x4007u +/* The Nearest Customer Bridge Group Address, 01-80-C2-00-00-[00,0B,0C,0D,0F] */ +#define BR_GROUPFWD_8021AD	0xB801u  /* Path to usermode spanning tree program */  #define BR_STP_PROG	"/sbin/bridge-stp" @@ -46,38 +48,32 @@ typedef __u16 port_id;  struct bridge_id  {  	unsigned char	prio[2]; -	unsigned char	addr[6]; +	unsigned char	addr[ETH_ALEN];  };  struct mac_addr  { -	unsigned char	addr[6]; -}; - -struct br_ip -{ -	union { -		__be32	ip4; -#if IS_ENABLED(CONFIG_IPV6) -		struct in6_addr ip6; -#endif -	} u; -	__be16		proto; -	__u16		vid; +	unsigned char	addr[ETH_ALEN];  };  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING  /* our own querier */ -struct bridge_mcast_query { +struct bridge_mcast_own_query {  	struct timer_list	timer;  	u32			startup_sent;  };  /* other querier */ -struct bridge_mcast_querier { +struct bridge_mcast_other_query {  	struct timer_list		timer;  	unsigned long			delay_time;  }; + +/* selected querier */ +struct bridge_mcast_querier { +	struct br_ip addr; +	struct net_bridge_port __rcu	*port; +};  #endif  struct net_port_vlans { @@ -104,6 +100,7 @@ struct net_bridge_fdb_entry  	mac_addr			addr;  	unsigned char			is_local;  	unsigned char			is_static; +	unsigned char			added_by_user;  	__u16				vlan_id;  }; @@ -126,7 +123,6 @@ struct net_bridge_mdb_entry  	struct timer_list		timer;  	struct br_ip			addr;  	bool				mglist; -	bool				timer_armed;  };  struct net_bridge_mdb_htable @@ -174,11 +170,13 @@ struct net_bridge_port  #define BR_ADMIN_COST		0x00000010  #define BR_LEARNING		0x00000020  #define BR_FLOOD		0x00000040 +#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING) +#define BR_PROMISC		0x00000080  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING -	struct bridge_mcast_query	ip4_query; +	struct bridge_mcast_own_query	ip4_own_query;  #if IS_ENABLED(CONFIG_IPV6) -	struct bridge_mcast_query	ip6_query; +	struct bridge_mcast_own_query	ip6_own_query;  #endif /* IS_ENABLED(CONFIG_IPV6) */  	unsigned char			multicast_router;  	struct timer_list		multicast_router_timer; @@ -198,37 +196,29 @@ struct net_bridge_port  #endif  }; +#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) +#define br_promisc_port(p) ((p)->flags & BR_PROMISC) +  #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)  static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)  { -	struct net_bridge_port *port = -			rcu_dereference_rtnl(dev->rx_handler_data); - -	return br_port_exists(dev) ? port : NULL; +	return rcu_dereference(dev->rx_handler_data);  } -static inline struct net_bridge_port *br_port_get_rtnl(struct net_device *dev) +static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device *dev)  {  	return br_port_exists(dev) ?  		rtnl_dereference(dev->rx_handler_data) : NULL;  } -struct br_cpu_netstats { -	u64			rx_packets; -	u64			rx_bytes; -	u64			tx_packets; -	u64			tx_bytes; -	struct u64_stats_sync	syncp; -}; -  struct net_bridge  {  	spinlock_t			lock;  	struct list_head		port_list;  	struct net_device		*dev; -	struct br_cpu_netstats __percpu *stats; +	struct pcpu_sw_netstats		__percpu *stats;  	spinlock_t			hash_lock;  	struct hlist_head		hash[BR_HASH_SIZE];  #ifdef CONFIG_BRIDGE_NETFILTER @@ -238,6 +228,7 @@ struct net_bridge  	bool				nf_call_arptables;  #endif  	u16				group_fwd_mask; +	u16				group_fwd_mask_required;  	/* STP */  	bridge_id			designated_root; @@ -252,6 +243,7 @@ struct net_bridge  	unsigned long			bridge_forward_delay;  	u8				group_addr[ETH_ALEN]; +	bool				group_addr_set;  	u16				root_port;  	enum { @@ -288,11 +280,13 @@ struct net_bridge  	struct hlist_head		router_list;  	struct timer_list		multicast_router_timer; +	struct bridge_mcast_other_query	ip4_other_query; +	struct bridge_mcast_own_query	ip4_own_query;  	struct bridge_mcast_querier	ip4_querier; -	struct bridge_mcast_query	ip4_query;  #if IS_ENABLED(CONFIG_IPV6) +	struct bridge_mcast_other_query	ip6_other_query; +	struct bridge_mcast_own_query	ip6_own_query;  	struct bridge_mcast_querier	ip6_querier; -	struct bridge_mcast_query	ip6_query;  #endif /* IS_ENABLED(CONFIG_IPV6) */  #endif @@ -301,8 +295,10 @@ struct net_bridge  	struct timer_list		topology_change_timer;  	struct timer_list		gc_timer;  	struct kobject			*ifobj; +	u32				auto_cnt;  #ifdef CONFIG_BRIDGE_VLAN_FILTERING  	u8				vlan_enabled; +	__be16				vlan_proto;  	struct net_port_vlans __rcu	*vlan_info;  #endif  }; @@ -338,8 +334,6 @@ struct br_input_skb_cb {  #define br_debug(br, format, args...)			\  	pr_debug("%s: " format,  (br)->dev->name, ##args) -extern struct notifier_block br_device_notifier; -  /* called under bridge lock */  static inline int br_is_root_bridge(const struct net_bridge *br)  { @@ -347,10 +341,9 @@ static inline int br_is_root_bridge(const struct net_bridge *br)  }  /* br_device.c */ -extern void br_dev_setup(struct net_device *dev); -extern void br_dev_delete(struct net_device *dev, struct list_head *list); -extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, -			       struct net_device *dev); +void br_dev_setup(struct net_device *dev); +void br_dev_delete(struct net_device *dev, struct list_head *list); +netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev);  #ifdef CONFIG_NET_POLL_CONTROLLER  static inline void br_netpoll_send_skb(const struct net_bridge_port *p,  				       struct sk_buff *skb) @@ -361,15 +354,15 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,  		netpoll_send_skb(np, skb);  } -extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp); -extern void br_netpoll_disable(struct net_bridge_port *p); +int br_netpoll_enable(struct net_bridge_port *p); +void br_netpoll_disable(struct net_bridge_port *p);  #else  static inline void br_netpoll_send_skb(const struct net_bridge_port *p,  				       struct sk_buff *skb)  {  } -static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) +static inline int br_netpoll_enable(struct net_bridge_port *p)  {  	return 0;  } @@ -380,116 +373,114 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)  #endif  /* br_fdb.c */ -extern int br_fdb_init(void); -extern void br_fdb_fini(void); -extern void br_fdb_flush(struct net_bridge *br); -extern void br_fdb_changeaddr(struct net_bridge_port *p, -			      const unsigned char *newaddr); -extern void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); -extern void br_fdb_cleanup(unsigned long arg); -extern void br_fdb_delete_by_port(struct net_bridge *br, -				  const struct net_bridge_port *p, int do_all); -extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, -						 const unsigned char *addr, -						 __u16 vid); -extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); -extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, -			  unsigned long count, unsigned long off); -extern int br_fdb_insert(struct net_bridge *br, -			 struct net_bridge_port *source, -			 const unsigned char *addr, -			 u16 vid); -extern void br_fdb_update(struct net_bridge *br, -			  struct net_bridge_port *source, -			  const unsigned char *addr, -			  u16 vid); -extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); - -extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], -			 struct net_device *dev, -			 const unsigned char *addr); -extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], -		      struct net_device *dev, -		      const unsigned char *addr, -		      u16 nlh_flags); -extern int br_fdb_dump(struct sk_buff *skb, -		       struct netlink_callback *cb, -		       struct net_device *dev, -		       int idx); +int br_fdb_init(void); +void br_fdb_fini(void); +void br_fdb_flush(struct net_bridge *br); +void br_fdb_find_delete_local(struct net_bridge *br, +			      const struct net_bridge_port *p, +			      const unsigned char *addr, u16 vid); +void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); +void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); +void br_fdb_cleanup(unsigned long arg); +void br_fdb_delete_by_port(struct net_bridge *br, +			   const struct net_bridge_port *p, int do_all); +struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, +					  const unsigned char *addr, __u16 vid); +int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); +int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, +		   unsigned long off); +int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, +		  const unsigned char *addr, u16 vid); +void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, +		   const unsigned char *addr, u16 vid, bool added_by_user); + +int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], +		  struct net_device *dev, const unsigned char *addr); +int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, +	       const unsigned char *addr, u16 nlh_flags); +int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, +		struct net_device *dev, int idx); +int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); +void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);  /* br_forward.c */ -extern void br_deliver(const struct net_bridge_port *to, -		struct sk_buff *skb); -extern int br_dev_queue_push_xmit(struct sk_buff *skb); -extern void br_forward(const struct net_bridge_port *to, +void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); +int br_dev_queue_push_xmit(struct sk_buff *skb); +void br_forward(const struct net_bridge_port *to,  		struct sk_buff *skb, struct sk_buff *skb0); -extern int br_forward_finish(struct sk_buff *skb); -extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, -			     bool unicast); -extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, -			     struct sk_buff *skb2, bool unicast); +int br_forward_finish(struct sk_buff *skb); +void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast); +void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, +		      struct sk_buff *skb2, bool unicast);  /* br_if.c */ -extern void br_port_carrier_check(struct net_bridge_port *p); -extern int br_add_bridge(struct net *net, const char *name); -extern int br_del_bridge(struct net *net, const char *name); -extern void br_net_exit(struct net *net); -extern int br_add_if(struct net_bridge *br, -	      struct net_device *dev); -extern int br_del_if(struct net_bridge *br, -	      struct net_device *dev); -extern int br_min_mtu(const struct net_bridge *br); -extern netdev_features_t br_features_recompute(struct net_bridge *br, -	netdev_features_t features); +void br_port_carrier_check(struct net_bridge_port *p); +int br_add_bridge(struct net *net, const char *name); +int br_del_bridge(struct net *net, const char *name); +int br_add_if(struct net_bridge *br, struct net_device *dev); +int br_del_if(struct net_bridge *br, struct net_device *dev); +int br_min_mtu(const struct net_bridge *br); +netdev_features_t br_features_recompute(struct net_bridge *br, +					netdev_features_t features); +void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); +void br_manage_promisc(struct net_bridge *br);  /* br_input.c */ -extern int br_handle_frame_finish(struct sk_buff *skb); -extern rx_handler_result_t br_handle_frame(struct sk_buff **pskb); +int br_handle_frame_finish(struct sk_buff *skb); +rx_handler_result_t br_handle_frame(struct sk_buff **pskb); + +static inline bool br_rx_handler_check_rcu(const struct net_device *dev) +{ +	return rcu_dereference(dev->rx_handler) == br_handle_frame; +} + +static inline struct net_bridge_port *br_port_get_check_rcu(const struct net_device *dev) +{ +	return br_rx_handler_check_rcu(dev) ? br_port_get_rcu(dev) : NULL; +}  /* br_ioctl.c */ -extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg); +int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, +			     void __user *arg);  /* br_multicast.c */  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING  extern unsigned int br_mdb_rehash_seq; -extern int br_multicast_rcv(struct net_bridge *br, -			    struct net_bridge_port *port, -			    struct sk_buff *skb); -extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, -					       struct sk_buff *skb, u16 vid); -extern void br_multicast_add_port(struct net_bridge_port *port); -extern void br_multicast_del_port(struct net_bridge_port *port); -extern void br_multicast_enable_port(struct net_bridge_port *port); -extern void br_multicast_disable_port(struct net_bridge_port *port); -extern void br_multicast_init(struct net_bridge *br); -extern void br_multicast_open(struct net_bridge *br); -extern void br_multicast_stop(struct net_bridge *br); -extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, -				 struct sk_buff *skb); -extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst, -				 struct sk_buff *skb, struct sk_buff *skb2); -extern int br_multicast_set_router(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_port_router(struct net_bridge_port *p, -					unsigned long val); -extern int br_multicast_toggle(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); -extern struct net_bridge_mdb_entry *br_mdb_ip_get( -				struct net_bridge_mdb_htable *mdb, -				struct br_ip *dst); -extern struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, -				struct net_bridge_port *port, struct br_ip *group); -extern void br_multicast_free_pg(struct rcu_head *head); -extern struct net_bridge_port_group *br_multicast_new_port_group( -				struct net_bridge_port *port, -				struct br_ip *group, -				struct net_bridge_port_group __rcu *next, -				unsigned char state); -extern void br_mdb_init(void); -extern void br_mdb_uninit(void); -extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, -			  struct br_ip *group, int type); +int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, +		     struct sk_buff *skb, u16 vid); +struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, +					struct sk_buff *skb, u16 vid); +void br_multicast_add_port(struct net_bridge_port *port); +void br_multicast_del_port(struct net_bridge_port *port); +void br_multicast_enable_port(struct net_bridge_port *port); +void br_multicast_disable_port(struct net_bridge_port *port); +void br_multicast_init(struct net_bridge *br); +void br_multicast_open(struct net_bridge *br); +void br_multicast_stop(struct net_bridge *br); +void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, +			  struct sk_buff *skb); +void br_multicast_forward(struct net_bridge_mdb_entry *mdst, +			  struct sk_buff *skb, struct sk_buff *skb2); +int br_multicast_set_router(struct net_bridge *br, unsigned long val); +int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val); +int br_multicast_toggle(struct net_bridge *br, unsigned long val); +int br_multicast_set_querier(struct net_bridge *br, unsigned long val); +int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); +struct net_bridge_mdb_entry * +br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst); +struct net_bridge_mdb_entry * +br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port, +		       struct br_ip *group); +void br_multicast_free_pg(struct rcu_head *head); +struct net_bridge_port_group * +br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, +			    struct net_bridge_port_group __rcu *next, +			    unsigned char state); +void br_mdb_init(void); +void br_mdb_uninit(void); +void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, +		   struct br_ip *group, int type);  #define mlock_dereference(X, br) \  	rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) @@ -503,7 +494,7 @@ static inline bool br_multicast_is_router(struct net_bridge *br)  static inline bool  __br_multicast_querier_exists(struct net_bridge *br, -			      struct bridge_mcast_querier *querier) +			      struct bridge_mcast_other_query *querier)  {  	return time_is_before_jiffies(querier->delay_time) &&  	       (br->multicast_querier || timer_pending(&querier->timer)); @@ -514,10 +505,10 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,  {  	switch (eth->h_proto) {  	case (htons(ETH_P_IP)): -		return __br_multicast_querier_exists(br, &br->ip4_querier); +		return __br_multicast_querier_exists(br, &br->ip4_other_query);  #if IS_ENABLED(CONFIG_IPV6)  	case (htons(ETH_P_IPV6)): -		return __br_multicast_querier_exists(br, &br->ip6_querier); +		return __br_multicast_querier_exists(br, &br->ip6_other_query);  #endif  	default:  		return false; @@ -526,7 +517,8 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,  #else  static inline int br_multicast_rcv(struct net_bridge *br,  				   struct net_bridge_port *port, -				   struct sk_buff *skb) +				   struct sk_buff *skb, +				   u16 vid)  {  	return 0;  } @@ -594,22 +586,26 @@ static inline void br_mdb_uninit(void)  /* br_vlan.c */  #ifdef CONFIG_BRIDGE_VLAN_FILTERING -extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, -			       struct sk_buff *skb, u16 *vid); -extern bool br_allowed_egress(struct net_bridge *br, -			      const struct net_port_vlans *v, -			      const struct sk_buff *skb); -extern struct sk_buff *br_handle_vlan(struct net_bridge *br, -				      const struct net_port_vlans *v, -				      struct sk_buff *skb); -extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); -extern int br_vlan_delete(struct net_bridge *br, u16 vid); -extern void br_vlan_flush(struct net_bridge *br); -extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); -extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); -extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); -extern void nbp_vlan_flush(struct net_bridge_port *port); -extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); +bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, +			struct sk_buff *skb, u16 *vid); +bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v, +		       const struct sk_buff *skb); +bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid); +struct sk_buff *br_handle_vlan(struct net_bridge *br, +			       const struct net_port_vlans *v, +			       struct sk_buff *skb); +int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); +int br_vlan_delete(struct net_bridge *br, u16 vid); +void br_vlan_flush(struct net_bridge *br); +bool br_vlan_find(struct net_bridge *br, u16 vid); +void br_recalculate_fwd_mask(struct net_bridge *br); +int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); +int br_vlan_set_proto(struct net_bridge *br, unsigned long val); +void br_vlan_init(struct net_bridge *br); +int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); +int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); +void nbp_vlan_flush(struct net_bridge_port *port); +bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);  static inline struct net_port_vlans *br_get_vlan_info(  						const struct net_bridge *br) @@ -646,11 +642,13 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)  	 * vid wasn't set  	 */  	smp_rmb(); -	return (v->pvid & VLAN_TAG_PRESENT) ? -			(v->pvid & ~VLAN_TAG_PRESENT) : -			VLAN_N_VID; +	return v->pvid ?: VLAN_N_VID;  } +static inline int br_vlan_enabled(struct net_bridge *br) +{ +	return br->vlan_enabled; +}  #else  static inline bool br_allowed_ingress(struct net_bridge *br,  				      struct net_port_vlans *v, @@ -667,6 +665,12 @@ static inline bool br_allowed_egress(struct net_bridge *br,  	return true;  } +static inline bool br_should_learn(struct net_bridge_port *p, +				   struct sk_buff *skb, u16 *vid) +{ +	return true; +} +  static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,  					     const struct net_port_vlans *v,  					     struct sk_buff *skb) @@ -688,6 +692,19 @@ static inline void br_vlan_flush(struct net_bridge *br)  {  } +static inline bool br_vlan_find(struct net_bridge *br, u16 vid) +{ +	return false; +} + +static inline void br_recalculate_fwd_mask(struct net_bridge *br) +{ +} + +static inline void br_vlan_init(struct net_bridge *br) +{ +} +  static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)  {  	return -EOPNOTSUPP; @@ -726,56 +743,58 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)  {  	return VLAN_N_VID;	/* Returns invalid vid */  } + +static inline int br_vlan_enabled(struct net_bridge *br) +{ +	return 0; +}  #endif  /* br_netfilter.c */  #ifdef CONFIG_BRIDGE_NETFILTER -extern int br_netfilter_init(void); -extern void br_netfilter_fini(void); -extern void br_netfilter_rtable_init(struct net_bridge *); +int br_netfilter_init(void); +void br_netfilter_fini(void); +void br_netfilter_rtable_init(struct net_bridge *);  #else  #define br_netfilter_init()	(0) -#define br_netfilter_fini()	do { } while(0) +#define br_netfilter_fini()	do { } while (0)  #define br_netfilter_rtable_init(x)  #endif  /* br_stp.c */ -extern void br_log_state(const struct net_bridge_port *p); -extern struct net_bridge_port *br_get_port(struct net_bridge *br, -					   u16 port_no); -extern void br_init_port(struct net_bridge_port *p); -extern void br_become_designated_port(struct net_bridge_port *p); +void br_log_state(const struct net_bridge_port *p); +struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no); +void br_init_port(struct net_bridge_port *p); +void br_become_designated_port(struct net_bridge_port *p); -extern int br_set_forward_delay(struct net_bridge *br, unsigned long x); -extern int br_set_hello_time(struct net_bridge *br, unsigned long x); -extern int br_set_max_age(struct net_bridge *br, unsigned long x); +void __br_set_forward_delay(struct net_bridge *br, unsigned long t); +int br_set_forward_delay(struct net_bridge *br, unsigned long x); +int br_set_hello_time(struct net_bridge *br, unsigned long x); +int br_set_max_age(struct net_bridge *br, unsigned long x);  /* br_stp_if.c */ -extern void br_stp_enable_bridge(struct net_bridge *br); -extern void br_stp_disable_bridge(struct net_bridge *br); -extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val); -extern void br_stp_enable_port(struct net_bridge_port *p); -extern void br_stp_disable_port(struct net_bridge_port *p); -extern bool br_stp_recalculate_bridge_id(struct net_bridge *br); -extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a); -extern void br_stp_set_bridge_priority(struct net_bridge *br, -				       u16 newprio); -extern int br_stp_set_port_priority(struct net_bridge_port *p, -				    unsigned long newprio); -extern int br_stp_set_path_cost(struct net_bridge_port *p, -				unsigned long path_cost); -extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); +void br_stp_enable_bridge(struct net_bridge *br); +void br_stp_disable_bridge(struct net_bridge *br); +void br_stp_set_enabled(struct net_bridge *br, unsigned long val); +void br_stp_enable_port(struct net_bridge_port *p); +void br_stp_disable_port(struct net_bridge_port *p); +bool br_stp_recalculate_bridge_id(struct net_bridge *br); +void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a); +void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio); +int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio); +int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost); +ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);  /* br_stp_bpdu.c */  struct stp_proto; -extern void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, -		       struct net_device *dev); +void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, +		struct net_device *dev);  /* br_stp_timer.c */ -extern void br_stp_timer_init(struct net_bridge *br); -extern void br_stp_port_timer_init(struct net_bridge_port *p); -extern unsigned long br_timer_value(const struct timer_list *timer); +void br_stp_timer_init(struct net_bridge *br); +void br_stp_port_timer_init(struct net_bridge_port *p); +unsigned long br_timer_value(const struct timer_list *timer);  /* br.c */  #if IS_ENABLED(CONFIG_ATM_LANE) @@ -784,23 +803,23 @@ extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr)  /* br_netlink.c */  extern struct rtnl_link_ops br_link_ops; -extern int br_netlink_init(void); -extern void br_netlink_fini(void); -extern void br_ifinfo_notify(int event, struct net_bridge_port *port); -extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); -extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); -extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, -		      struct net_device *dev, u32 filter_mask); +int br_netlink_init(void); +void br_netlink_fini(void); +void br_ifinfo_notify(int event, struct net_bridge_port *port); +int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); +int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); +int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, +	       u32 filter_mask);  #ifdef CONFIG_SYSFS  /* br_sysfs_if.c */  extern const struct sysfs_ops brport_sysfs_ops; -extern int br_sysfs_addif(struct net_bridge_port *p); -extern int br_sysfs_renameif(struct net_bridge_port *p); +int br_sysfs_addif(struct net_bridge_port *p); +int br_sysfs_renameif(struct net_bridge_port *p);  /* br_sysfs_br.c */ -extern int br_sysfs_addbr(struct net_device *dev); -extern void br_sysfs_delbr(struct net_device *dev); +int br_sysfs_addbr(struct net_device *dev); +void br_sysfs_delbr(struct net_device *dev);  #else diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 0c0fe36e7aa..2fe910c4e17 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -51,19 +51,19 @@ static inline int br_is_designated_port(const struct net_bridge_port *p)  /* br_stp.c */ -extern void br_become_root_bridge(struct net_bridge *br); -extern void br_config_bpdu_generation(struct net_bridge *); -extern void br_configuration_update(struct net_bridge *); -extern void br_port_state_selection(struct net_bridge *); -extern void br_received_config_bpdu(struct net_bridge_port *p, -				    const struct br_config_bpdu *bpdu); -extern void br_received_tcn_bpdu(struct net_bridge_port *p); -extern void br_transmit_config(struct net_bridge_port *p); -extern void br_transmit_tcn(struct net_bridge *br); -extern void br_topology_change_detection(struct net_bridge *br); +void br_become_root_bridge(struct net_bridge *br); +void br_config_bpdu_generation(struct net_bridge *); +void br_configuration_update(struct net_bridge *); +void br_port_state_selection(struct net_bridge *); +void br_received_config_bpdu(struct net_bridge_port *p, +			     const struct br_config_bpdu *bpdu); +void br_received_tcn_bpdu(struct net_bridge_port *p); +void br_transmit_config(struct net_bridge_port *p); +void br_transmit_tcn(struct net_bridge *br); +void br_topology_change_detection(struct net_bridge *br);  /* br_stp_bpdu.c */ -extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); -extern void br_send_tcn_bpdu(struct net_bridge_port *); +void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); +void br_send_tcn_bpdu(struct net_bridge_port *);  #endif diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 1c0a50f1322..3c86f0538cb 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -209,7 +209,7 @@ static void br_record_config_information(struct net_bridge_port *p,  	p->designated_age = jiffies - bpdu->message_age;  	mod_timer(&p->message_age_timer, jiffies -		  + (p->br->max_age - bpdu->message_age)); +		  + (bpdu->max_age - bpdu->message_age));  }  /* called under bridge lock */ @@ -544,18 +544,27 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)  } +void __br_set_forward_delay(struct net_bridge *br, unsigned long t) +{ +	br->bridge_forward_delay = t; +	if (br_is_root_bridge(br)) +		br->forward_delay = br->bridge_forward_delay; +} +  int br_set_forward_delay(struct net_bridge *br, unsigned long val)  {  	unsigned long t = clock_t_to_jiffies(val); +	int err = -ERANGE; +	spin_lock_bh(&br->lock);  	if (br->stp_enabled != BR_NO_STP &&  	    (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) -		return -ERANGE; +		goto unlock; -	spin_lock_bh(&br->lock); -	br->bridge_forward_delay = t; -	if (br_is_root_bridge(br)) -		br->forward_delay = br->bridge_forward_delay; +	__br_set_forward_delay(br, t); +	err = 0; + +unlock:  	spin_unlock_bh(&br->lock); -	return 0; +	return err;  } diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 8660ea3be70..bdb459d21ad 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -153,7 +153,7 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,  	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)  		goto err; -	p = br_port_get_rcu(dev); +	p = br_port_get_check_rcu(dev);  	if (!p)  		goto err; diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index d45e760141b..189ba1e7d85 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -129,6 +129,14 @@ static void br_stp_start(struct net_bridge *br)  	char *envp[] = { NULL };  	r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC); + +	spin_lock_bh(&br->lock); + +	if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY) +		__br_set_forward_delay(br, BR_MIN_FORWARD_DELAY); +	else if (br->bridge_forward_delay > BR_MAX_FORWARD_DELAY) +		__br_set_forward_delay(br, BR_MAX_FORWARD_DELAY); +  	if (r == 0) {  		br->stp_enabled = BR_USER_STP;  		br_debug(br, "userspace STP started\n"); @@ -137,10 +145,10 @@ static void br_stp_start(struct net_bridge *br)  		br_debug(br, "using kernel STP\n");  		/* To start timers on any ports left in blocking */ -		spin_lock_bh(&br->lock);  		br_port_state_selection(br); -		spin_unlock_bh(&br->lock);  	} + +	spin_unlock_bh(&br->lock);  }  static void br_stp_stop(struct net_bridge *br) @@ -186,6 +194,8 @@ void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)  	wasroot = br_is_root_bridge(br); +	br_fdb_change_mac_address(br, addr); +  	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);  	memcpy(br->bridge_id.addr, addr, ETH_ALEN);  	memcpy(br->dev->dev_addr, addr, ETH_ALEN); diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 950663d4d33..558c46d19e0 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -110,7 +110,7 @@ static void br_tcn_timer_expired(unsigned long arg)  	if (!br_is_root_bridge(br) && (br->dev->flags & IFF_UP)) {  		br_transmit_tcn(br); -		mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time); +		mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);  	}  	spin_unlock(&br->lock);  } diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 3b9637fb793..c9e2572b15f 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -49,53 +49,51 @@ static ssize_t store_bridge_parm(struct device *d,  } -static ssize_t show_forward_delay(struct device *d, +static ssize_t forward_delay_show(struct device *d,  				  struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));  } -static ssize_t store_forward_delay(struct device *d, +static ssize_t forward_delay_store(struct device *d,  				   struct device_attribute *attr,  				   const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_set_forward_delay);  } -static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, -		   show_forward_delay, store_forward_delay); +static DEVICE_ATTR_RW(forward_delay); -static ssize_t show_hello_time(struct device *d, struct device_attribute *attr, +static ssize_t hello_time_show(struct device *d, struct device_attribute *attr,  			       char *buf)  {  	return sprintf(buf, "%lu\n",  		       jiffies_to_clock_t(to_bridge(d)->hello_time));  } -static ssize_t store_hello_time(struct device *d, +static ssize_t hello_time_store(struct device *d,  				struct device_attribute *attr, const char *buf,  				size_t len)  {  	return store_bridge_parm(d, buf, len, br_set_hello_time);  } -static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, -		   store_hello_time); +static DEVICE_ATTR_RW(hello_time); -static ssize_t show_max_age(struct device *d, struct device_attribute *attr, +static ssize_t max_age_show(struct device *d, struct device_attribute *attr,  			    char *buf)  {  	return sprintf(buf, "%lu\n",  		       jiffies_to_clock_t(to_bridge(d)->max_age));  } -static ssize_t store_max_age(struct device *d, struct device_attribute *attr, +static ssize_t max_age_store(struct device *d, struct device_attribute *attr,  			     const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_set_max_age);  } -static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age); +static DEVICE_ATTR_RW(max_age); -static ssize_t show_ageing_time(struct device *d, +static ssize_t ageing_time_show(struct device *d,  				struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -108,16 +106,15 @@ static int set_ageing_time(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_ageing_time(struct device *d, +static ssize_t ageing_time_store(struct device *d,  				 struct device_attribute *attr,  				 const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_ageing_time);  } -static DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time, -		   store_ageing_time); +static DEVICE_ATTR_RW(ageing_time); -static ssize_t show_stp_state(struct device *d, +static ssize_t stp_state_show(struct device *d,  			      struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -125,7 +122,7 @@ static ssize_t show_stp_state(struct device *d,  } -static ssize_t store_stp_state(struct device *d, +static ssize_t stp_state_store(struct device *d,  			       struct device_attribute *attr, const char *buf,  			       size_t len)  { @@ -147,20 +144,21 @@ static ssize_t store_stp_state(struct device *d,  	return len;  } -static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, -		   store_stp_state); +static DEVICE_ATTR_RW(stp_state); -static ssize_t show_group_fwd_mask(struct device *d, -			      struct device_attribute *attr, char *buf) +static ssize_t group_fwd_mask_show(struct device *d, +				   struct device_attribute *attr, +				   char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%#x\n", br->group_fwd_mask);  } -static ssize_t store_group_fwd_mask(struct device *d, -			       struct device_attribute *attr, const char *buf, -			       size_t len) +static ssize_t group_fwd_mask_store(struct device *d, +				    struct device_attribute *attr, +				    const char *buf, +				    size_t len)  {  	struct net_bridge *br = to_bridge(d);  	char *endp; @@ -180,10 +178,9 @@ static ssize_t store_group_fwd_mask(struct device *d,  	return len;  } -static DEVICE_ATTR(group_fwd_mask, S_IRUGO | S_IWUSR, show_group_fwd_mask, -		   store_group_fwd_mask); +static DEVICE_ATTR_RW(group_fwd_mask); -static ssize_t show_priority(struct device *d, struct device_attribute *attr, +static ssize_t priority_show(struct device *d, struct device_attribute *attr,  			     char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -197,93 +194,91 @@ static int set_priority(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_priority(struct device *d, struct device_attribute *attr, -			       const char *buf, size_t len) +static ssize_t priority_store(struct device *d, struct device_attribute *attr, +			      const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_priority);  } -static DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority); +static DEVICE_ATTR_RW(priority); -static ssize_t show_root_id(struct device *d, struct device_attribute *attr, +static ssize_t root_id_show(struct device *d, struct device_attribute *attr,  			    char *buf)  {  	return br_show_bridge_id(buf, &to_bridge(d)->designated_root);  } -static DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL); +static DEVICE_ATTR_RO(root_id); -static ssize_t show_bridge_id(struct device *d, struct device_attribute *attr, +static ssize_t bridge_id_show(struct device *d, struct device_attribute *attr,  			      char *buf)  {  	return br_show_bridge_id(buf, &to_bridge(d)->bridge_id);  } -static DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL); +static DEVICE_ATTR_RO(bridge_id); -static ssize_t show_root_port(struct device *d, struct device_attribute *attr, +static ssize_t root_port_show(struct device *d, struct device_attribute *attr,  			      char *buf)  {  	return sprintf(buf, "%d\n", to_bridge(d)->root_port);  } -static DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL); +static DEVICE_ATTR_RO(root_port); -static ssize_t show_root_path_cost(struct device *d, +static ssize_t root_path_cost_show(struct device *d,  				   struct device_attribute *attr, char *buf)  {  	return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);  } -static DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL); +static DEVICE_ATTR_RO(root_path_cost); -static ssize_t show_topology_change(struct device *d, +static ssize_t topology_change_show(struct device *d,  				    struct device_attribute *attr, char *buf)  {  	return sprintf(buf, "%d\n", to_bridge(d)->topology_change);  } -static DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL); +static DEVICE_ATTR_RO(topology_change); -static ssize_t show_topology_change_detected(struct device *d, +static ssize_t topology_change_detected_show(struct device *d,  					     struct device_attribute *attr,  					     char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%d\n", br->topology_change_detected);  } -static DEVICE_ATTR(topology_change_detected, S_IRUGO, -		   show_topology_change_detected, NULL); +static DEVICE_ATTR_RO(topology_change_detected); -static ssize_t show_hello_timer(struct device *d, +static ssize_t hello_timer_show(struct device *d,  				struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));  } -static DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL); +static DEVICE_ATTR_RO(hello_timer); -static ssize_t show_tcn_timer(struct device *d, struct device_attribute *attr, +static ssize_t tcn_timer_show(struct device *d, struct device_attribute *attr,  			      char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));  } -static DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL); +static DEVICE_ATTR_RO(tcn_timer); -static ssize_t show_topology_change_timer(struct device *d, +static ssize_t topology_change_timer_show(struct device *d,  					  struct device_attribute *attr,  					  char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));  } -static DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer, -		   NULL); +static DEVICE_ATTR_RO(topology_change_timer); -static ssize_t show_gc_timer(struct device *d, struct device_attribute *attr, +static ssize_t gc_timer_show(struct device *d, struct device_attribute *attr,  			     char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));  } -static DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL); +static DEVICE_ATTR_RO(gc_timer); -static ssize_t show_group_addr(struct device *d, +static ssize_t group_addr_show(struct device *d,  			       struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -293,7 +288,7 @@ static ssize_t show_group_addr(struct device *d,  		       br->group_addr[4], br->group_addr[5]);  } -static ssize_t store_group_addr(struct device *d, +static ssize_t group_addr_store(struct device *d,  				struct device_attribute *attr,  				const char *buf, size_t len)  { @@ -317,17 +312,25 @@ static ssize_t store_group_addr(struct device *d,  	    new_addr[5] == 3)		/* 802.1X PAE address */  		return -EINVAL; +	if (!rtnl_trylock()) +		return restart_syscall(); +  	spin_lock_bh(&br->lock);  	for (i = 0; i < 6; i++)  		br->group_addr[i] = new_addr[i];  	spin_unlock_bh(&br->lock); + +	br->group_addr_set = true; +	br_recalculate_fwd_mask(br); + +	rtnl_unlock(); +  	return len;  } -static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR, -		   show_group_addr, store_group_addr); +static DEVICE_ATTR_RW(group_addr); -static ssize_t store_flush(struct device *d, +static ssize_t flush_store(struct device *d,  			   struct device_attribute *attr,  			   const char *buf, size_t len)  { @@ -339,26 +342,25 @@ static ssize_t store_flush(struct device *d,  	br_fdb_flush(br);  	return len;  } -static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush); +static DEVICE_ATTR_WO(flush);  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING -static ssize_t show_multicast_router(struct device *d, +static ssize_t multicast_router_show(struct device *d,  				     struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%d\n", br->multicast_router);  } -static ssize_t store_multicast_router(struct device *d, +static ssize_t multicast_router_store(struct device *d,  				      struct device_attribute *attr,  				      const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_multicast_set_router);  } -static DEVICE_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router, -		   store_multicast_router); +static DEVICE_ATTR_RW(multicast_router); -static ssize_t show_multicast_snooping(struct device *d, +static ssize_t multicast_snooping_show(struct device *d,  				       struct device_attribute *attr,  				       char *buf)  { @@ -366,18 +368,17 @@ static ssize_t show_multicast_snooping(struct device *d,  	return sprintf(buf, "%d\n", !br->multicast_disabled);  } -static ssize_t store_multicast_snooping(struct device *d, +static ssize_t multicast_snooping_store(struct device *d,  					struct device_attribute *attr,  					const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_multicast_toggle);  } -static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR, -		   show_multicast_snooping, store_multicast_snooping); +static DEVICE_ATTR_RW(multicast_snooping); -static ssize_t show_multicast_query_use_ifaddr(struct device *d, -				      struct device_attribute *attr, -				      char *buf) +static ssize_t multicast_query_use_ifaddr_show(struct device *d, +					       struct device_attribute *attr, +					       char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%d\n", br->multicast_query_use_ifaddr); @@ -390,17 +391,15 @@ static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val)  }  static ssize_t -store_multicast_query_use_ifaddr(struct device *d, +multicast_query_use_ifaddr_store(struct device *d,  				 struct device_attribute *attr,  				 const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_query_use_ifaddr);  } -static DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR, -		   show_multicast_query_use_ifaddr, -		   store_multicast_query_use_ifaddr); +static DEVICE_ATTR_RW(multicast_query_use_ifaddr); -static ssize_t show_multicast_querier(struct device *d, +static ssize_t multicast_querier_show(struct device *d,  				      struct device_attribute *attr,  				      char *buf)  { @@ -408,16 +407,15 @@ static ssize_t show_multicast_querier(struct device *d,  	return sprintf(buf, "%d\n", br->multicast_querier);  } -static ssize_t store_multicast_querier(struct device *d, +static ssize_t multicast_querier_store(struct device *d,  				       struct device_attribute *attr,  				       const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_multicast_set_querier);  } -static DEVICE_ATTR(multicast_querier, S_IRUGO | S_IWUSR, -		   show_multicast_querier, store_multicast_querier); +static DEVICE_ATTR_RW(multicast_querier); -static ssize_t show_hash_elasticity(struct device *d, +static ssize_t hash_elasticity_show(struct device *d,  				    struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -430,31 +428,29 @@ static int set_elasticity(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_hash_elasticity(struct device *d, +static ssize_t hash_elasticity_store(struct device *d,  				     struct device_attribute *attr,  				     const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_elasticity);  } -static DEVICE_ATTR(hash_elasticity, S_IRUGO | S_IWUSR, show_hash_elasticity, -		   store_hash_elasticity); +static DEVICE_ATTR_RW(hash_elasticity); -static ssize_t show_hash_max(struct device *d, struct device_attribute *attr, +static ssize_t hash_max_show(struct device *d, struct device_attribute *attr,  			     char *buf)  {  	struct net_bridge *br = to_bridge(d);  	return sprintf(buf, "%u\n", br->hash_max);  } -static ssize_t store_hash_max(struct device *d, struct device_attribute *attr, +static ssize_t hash_max_store(struct device *d, struct device_attribute *attr,  			      const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_multicast_set_hash_max);  } -static DEVICE_ATTR(hash_max, S_IRUGO | S_IWUSR, show_hash_max, -		   store_hash_max); +static DEVICE_ATTR_RW(hash_max); -static ssize_t show_multicast_last_member_count(struct device *d, +static ssize_t multicast_last_member_count_show(struct device *d,  						struct device_attribute *attr,  						char *buf)  { @@ -468,17 +464,15 @@ static int set_last_member_count(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_last_member_count(struct device *d, +static ssize_t multicast_last_member_count_store(struct device *d,  						 struct device_attribute *attr,  						 const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_last_member_count);  } -static DEVICE_ATTR(multicast_last_member_count, S_IRUGO | S_IWUSR, -		   show_multicast_last_member_count, -		   store_multicast_last_member_count); +static DEVICE_ATTR_RW(multicast_last_member_count); -static ssize_t show_multicast_startup_query_count( +static ssize_t multicast_startup_query_count_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -491,17 +485,15 @@ static int set_startup_query_count(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_startup_query_count( +static ssize_t multicast_startup_query_count_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_startup_query_count);  } -static DEVICE_ATTR(multicast_startup_query_count, S_IRUGO | S_IWUSR, -		   show_multicast_startup_query_count, -		   store_multicast_startup_query_count); +static DEVICE_ATTR_RW(multicast_startup_query_count); -static ssize_t show_multicast_last_member_interval( +static ssize_t multicast_last_member_interval_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -515,17 +507,15 @@ static int set_last_member_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_last_member_interval( +static ssize_t multicast_last_member_interval_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_last_member_interval);  } -static DEVICE_ATTR(multicast_last_member_interval, S_IRUGO | S_IWUSR, -		   show_multicast_last_member_interval, -		   store_multicast_last_member_interval); +static DEVICE_ATTR_RW(multicast_last_member_interval); -static ssize_t show_multicast_membership_interval( +static ssize_t multicast_membership_interval_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -539,17 +529,15 @@ static int set_membership_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_membership_interval( +static ssize_t multicast_membership_interval_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_membership_interval);  } -static DEVICE_ATTR(multicast_membership_interval, S_IRUGO | S_IWUSR, -		   show_multicast_membership_interval, -		   store_multicast_membership_interval); +static DEVICE_ATTR_RW(multicast_membership_interval); -static ssize_t show_multicast_querier_interval(struct device *d, +static ssize_t multicast_querier_interval_show(struct device *d,  					       struct device_attribute *attr,  					       char *buf)  { @@ -564,17 +552,15 @@ static int set_querier_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_querier_interval(struct device *d, +static ssize_t multicast_querier_interval_store(struct device *d,  						struct device_attribute *attr,  						const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_querier_interval);  } -static DEVICE_ATTR(multicast_querier_interval, S_IRUGO | S_IWUSR, -		   show_multicast_querier_interval, -		   store_multicast_querier_interval); +static DEVICE_ATTR_RW(multicast_querier_interval); -static ssize_t show_multicast_query_interval(struct device *d, +static ssize_t multicast_query_interval_show(struct device *d,  					     struct device_attribute *attr,  					     char *buf)  { @@ -589,17 +575,15 @@ static int set_query_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_query_interval(struct device *d, +static ssize_t multicast_query_interval_store(struct device *d,  					      struct device_attribute *attr,  					      const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, set_query_interval);  } -static DEVICE_ATTR(multicast_query_interval, S_IRUGO | S_IWUSR, -		   show_multicast_query_interval, -		   store_multicast_query_interval); +static DEVICE_ATTR_RW(multicast_query_interval); -static ssize_t show_multicast_query_response_interval( +static ssize_t multicast_query_response_interval_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -614,17 +598,15 @@ static int set_query_response_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_query_response_interval( +static ssize_t multicast_query_response_interval_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_query_response_interval);  } -static DEVICE_ATTR(multicast_query_response_interval, S_IRUGO | S_IWUSR, -		   show_multicast_query_response_interval, -		   store_multicast_query_response_interval); +static DEVICE_ATTR_RW(multicast_query_response_interval); -static ssize_t show_multicast_startup_query_interval( +static ssize_t multicast_startup_query_interval_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -639,18 +621,16 @@ static int set_startup_query_interval(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_multicast_startup_query_interval( +static ssize_t multicast_startup_query_interval_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_startup_query_interval);  } -static DEVICE_ATTR(multicast_startup_query_interval, S_IRUGO | S_IWUSR, -		   show_multicast_startup_query_interval, -		   store_multicast_startup_query_interval); +static DEVICE_ATTR_RW(multicast_startup_query_interval);  #endif  #ifdef CONFIG_BRIDGE_NETFILTER -static ssize_t show_nf_call_iptables( +static ssize_t nf_call_iptables_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -663,16 +643,15 @@ static int set_nf_call_iptables(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_nf_call_iptables( +static ssize_t nf_call_iptables_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_nf_call_iptables);  } -static DEVICE_ATTR(nf_call_iptables, S_IRUGO | S_IWUSR, -		   show_nf_call_iptables, store_nf_call_iptables); +static DEVICE_ATTR_RW(nf_call_iptables); -static ssize_t show_nf_call_ip6tables( +static ssize_t nf_call_ip6tables_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -685,16 +664,15 @@ static int set_nf_call_ip6tables(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_nf_call_ip6tables( +static ssize_t nf_call_ip6tables_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_nf_call_ip6tables);  } -static DEVICE_ATTR(nf_call_ip6tables, S_IRUGO | S_IWUSR, -		   show_nf_call_ip6tables, store_nf_call_ip6tables); +static DEVICE_ATTR_RW(nf_call_ip6tables); -static ssize_t show_nf_call_arptables( +static ssize_t nf_call_arptables_show(  	struct device *d, struct device_attribute *attr, char *buf)  {  	struct net_bridge *br = to_bridge(d); @@ -707,17 +685,16 @@ static int set_nf_call_arptables(struct net_bridge *br, unsigned long val)  	return 0;  } -static ssize_t store_nf_call_arptables( +static ssize_t nf_call_arptables_store(  	struct device *d, struct device_attribute *attr, const char *buf,  	size_t len)  {  	return store_bridge_parm(d, buf, len, set_nf_call_arptables);  } -static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, -		   show_nf_call_arptables, store_nf_call_arptables); +static DEVICE_ATTR_RW(nf_call_arptables);  #endif  #ifdef CONFIG_BRIDGE_VLAN_FILTERING -static ssize_t show_vlan_filtering(struct device *d, +static ssize_t vlan_filtering_show(struct device *d,  				   struct device_attribute *attr,  				   char *buf)  { @@ -725,14 +702,29 @@ static ssize_t show_vlan_filtering(struct device *d,  	return sprintf(buf, "%d\n", br->vlan_enabled);  } -static ssize_t store_vlan_filtering(struct device *d, +static ssize_t vlan_filtering_store(struct device *d,  				    struct device_attribute *attr,  				    const char *buf, size_t len)  {  	return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);  } -static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR, -		   show_vlan_filtering, store_vlan_filtering); +static DEVICE_ATTR_RW(vlan_filtering); + +static ssize_t vlan_protocol_show(struct device *d, +				  struct device_attribute *attr, +				  char *buf) +{ +	struct net_bridge *br = to_bridge(d); +	return sprintf(buf, "%#06x\n", ntohs(br->vlan_proto)); +} + +static ssize_t vlan_protocol_store(struct device *d, +				   struct device_attribute *attr, +				   const char *buf, size_t len) +{ +	return store_bridge_parm(d, buf, len, br_vlan_set_proto); +} +static DEVICE_ATTR_RW(vlan_protocol);  #endif  static struct attribute *bridge_attrs[] = { @@ -778,6 +770,7 @@ static struct attribute *bridge_attrs[] = {  #endif  #ifdef CONFIG_BRIDGE_VLAN_FILTERING  	&dev_attr_vlan_filtering.attr, +	&dev_attr_vlan_protocol.attr,  #endif  	NULL  }; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 2a2cdb756d5..e561cd59b8a 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -26,7 +26,7 @@ struct brport_attribute {  	int (*store)(struct net_bridge_port *, unsigned long);  }; -#define BRPORT_ATTR(_name,_mode,_show,_store)		        \ +#define BRPORT_ATTR(_name, _mode, _show, _store)		\  const struct brport_attribute brport_attr_##_name = { 	        \  	.attr = {.name = __stringify(_name), 			\  		 .mode = _mode },				\ @@ -41,20 +41,30 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \  }								\  static int store_##_name(struct net_bridge_port *p, unsigned long v) \  {								\ -	unsigned long flags = p->flags;				\ -	if (v)							\ -		flags |= _mask;					\ -	else							\ -		flags &= ~_mask;				\ -	if (flags != p->flags) {				\ -		p->flags = flags;				\ -		br_ifinfo_notify(RTM_NEWLINK, p);		\ -	}							\ -	return 0;						\ +	return store_flag(p, v, _mask);				\  }								\  static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,			\  		   show_##_name, store_##_name) +static int store_flag(struct net_bridge_port *p, unsigned long v, +		      unsigned long mask) +{ +	unsigned long flags; + +	flags = p->flags; + +	if (v) +		flags |= mask; +	else +		flags &= ~mask; + +	if (flags != p->flags) { +		p->flags = flags; +		br_port_flags_change(p, mask); +		br_ifinfo_notify(RTM_NEWLINK, p); +	} +	return 0; +}  static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)  { @@ -209,21 +219,21 @@ static const struct brport_attribute *brport_attrs[] = {  #define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)  #define to_brport(obj)	container_of(obj, struct net_bridge_port, kobj) -static ssize_t brport_show(struct kobject * kobj, -			   struct attribute * attr, char * buf) +static ssize_t brport_show(struct kobject *kobj, +			   struct attribute *attr, char *buf)  { -	struct brport_attribute * brport_attr = to_brport_attr(attr); -	struct net_bridge_port * p = to_brport(kobj); +	struct brport_attribute *brport_attr = to_brport_attr(attr); +	struct net_bridge_port *p = to_brport(kobj);  	return brport_attr->show(p, buf);  } -static ssize_t brport_store(struct kobject * kobj, -			    struct attribute * attr, -			    const char * buf, size_t count) +static ssize_t brport_store(struct kobject *kobj, +			    struct attribute *attr, +			    const char *buf, size_t count)  { -	struct brport_attribute * brport_attr = to_brport_attr(attr); -	struct net_bridge_port * p = to_brport(kobj); +	struct brport_attribute *brport_attr = to_brport_attr(attr); +	struct net_bridge_port *p = to_brport(kobj);  	ssize_t ret = -EINVAL;  	char *endp;  	unsigned long val; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 9a9ffe7e401..2b2774fe070 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -34,7 +34,6 @@ static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)  static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)  { -	const struct net_device_ops *ops;  	struct net_bridge_port *p = NULL;  	struct net_bridge *br;  	struct net_device *dev; @@ -45,37 +44,32 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)  		return 0;  	} -	if (vid) { -		if (v->port_idx) { -			p = v->parent.port; -			br = p->br; -			dev = p->dev; -		} else { -			br = v->parent.br; -			dev = br->dev; -		} -		ops = dev->netdev_ops; - -		if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { -			/* Add VLAN to the device filter if it is supported. -			 * Stricly speaking, this is not necessary now, since -			 * devices are made promiscuous by the bridge, but if -			 * that ever changes this code will allow tagged -			 * traffic to enter the bridge. -			 */ -			err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q), -						       vid); -			if (err) -				return err; -		} +	if (v->port_idx) { +		p = v->parent.port; +		br = p->br; +		dev = p->dev; +	} else { +		br = v->parent.br; +		dev = br->dev; +	} -		err = br_fdb_insert(br, p, dev->dev_addr, vid); -		if (err) { -			br_err(br, "failed insert local address into bridge " -			       "forwarding table\n"); -			goto out_filt; -		} +	if (p) { +		/* Add VLAN to the device filter if it is supported. +		 * Stricly speaking, this is not necessary now, since +		 * devices are made promiscuous by the bridge, but if +		 * that ever changes this code will allow tagged +		 * traffic to enter the bridge. +		 */ +		err = vlan_vid_add(dev, br->vlan_proto, vid); +		if (err) +			return err; +	} +	err = br_fdb_insert(br, p, dev->dev_addr, vid); +	if (err) { +		br_err(br, "failed insert local address into bridge " +		       "forwarding table\n"); +		goto out_filt;  	}  	set_bit(vid, v->vlan_bitmap); @@ -85,8 +79,8 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)  	return 0;  out_filt: -	if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) -		ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid); +	if (p) +		vlan_vid_del(dev, br->vlan_proto, vid);  	return err;  } @@ -98,21 +92,18 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)  	__vlan_delete_pvid(v, vid);  	clear_bit(vid, v->untagged_bitmap); -	if (v->port_idx && vid) { -		struct net_device *dev = v->parent.port->dev; -		const struct net_device_ops *ops = dev->netdev_ops; - -		if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) -			ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid); +	if (v->port_idx) { +		struct net_bridge_port *p = v->parent.port; +		vlan_vid_del(p->dev, p->br->vlan_proto, vid);  	}  	clear_bit(vid, v->vlan_bitmap);  	v->num_vlans--;  	if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) {  		if (v->port_idx) -			rcu_assign_pointer(v->parent.port->vlan_info, NULL); +			RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);  		else -			rcu_assign_pointer(v->parent.br->vlan_info, NULL); +			RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);  		kfree_rcu(v, rcu);  	}  	return 0; @@ -124,28 +115,12 @@ static void __vlan_flush(struct net_port_vlans *v)  	v->pvid = 0;  	bitmap_zero(v->vlan_bitmap, VLAN_N_VID);  	if (v->port_idx) -		rcu_assign_pointer(v->parent.port->vlan_info, NULL); +		RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);  	else -		rcu_assign_pointer(v->parent.br->vlan_info, NULL); +		RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);  	kfree_rcu(v, rcu);  } -/* Strip the tag from the packet.  Will return skb with tci set 0.  */ -static struct sk_buff *br_vlan_untag(struct sk_buff *skb) -{ -	if (skb->protocol != htons(ETH_P_8021Q)) { -		skb->vlan_tci = 0; -		return skb; -	} - -	skb->vlan_tci = 0; -	skb = vlan_untag(skb); -	if (skb) -		skb->vlan_tci = 0; - -	return skb; -} -  struct sk_buff *br_handle_vlan(struct net_bridge *br,  			       const struct net_port_vlans *pv,  			       struct sk_buff *skb) @@ -155,34 +130,27 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,  	if (!br->vlan_enabled)  		goto out; +	/* Vlan filter table must be configured at this point.  The +	 * only exception is the bridge is set in promisc mode and the +	 * packet is destined for the bridge device.  In this case +	 * pass the packet as is. +	 */ +	if (!pv) { +		if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) { +			goto out; +		} else { +			kfree_skb(skb); +			return NULL; +		} +	} +  	/* At this point, we know that the frame was filtered and contains  	 * a valid vlan id.  If the vlan id is set in the untagged bitmap, -	 * send untagged; otherwise, send taged. +	 * send untagged; otherwise, send tagged.  	 */  	br_vlan_get_tag(skb, &vid);  	if (test_bit(vid, pv->untagged_bitmap)) -		skb = br_vlan_untag(skb); -	else { -		/* Egress policy says "send tagged".  If output device -		 * is the  bridge, we need to add the VLAN header -		 * ourselves since we'll be going through the RX path. -		 * Sending to ports puts the frame on the TX path and -		 * we let dev_hard_start_xmit() add the header. -		 */ -		if (skb->protocol != htons(ETH_P_8021Q) && -		    pv->port_idx == 0) { -			/* vlan_put_tag expects skb->data to point to -			 * mac header. -			 */ -			skb_push(skb, ETH_HLEN); -			skb = __vlan_put_tag(skb, skb->vlan_proto, skb->vlan_tci); -			if (!skb) -				goto out; -			/* put skb->data back to where it was */ -			skb_pull(skb, ETH_HLEN); -			skb->vlan_tci = 0; -		} -	} +		skb->vlan_tci = 0;  out:  	return skb; @@ -192,6 +160,9 @@ out:  bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,  			struct sk_buff *skb, u16 *vid)  { +	bool tagged; +	__be16 proto; +  	/* If VLAN filtering is disabled on the bridge, all packets are  	 * permitted.  	 */ @@ -202,29 +173,76 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,  	 * rejected.  	 */  	if (!v) -		return false; +		goto drop; -	if (br_vlan_get_tag(skb, vid)) { +	proto = br->vlan_proto; + +	/* If vlan tx offload is disabled on bridge device and frame was +	 * sent from vlan device on the bridge device, it does not have +	 * HW accelerated vlan tag. +	 */ +	if (unlikely(!vlan_tx_tag_present(skb) && +		     skb->protocol == proto)) { +		skb = vlan_untag(skb); +		if (unlikely(!skb)) +			return false; +	} + +	if (!br_vlan_get_tag(skb, vid)) { +		/* Tagged frame */ +		if (skb->vlan_proto != proto) { +			/* Protocol-mismatch, empty out vlan_tci for new tag */ +			skb_push(skb, ETH_HLEN); +			skb = __vlan_put_tag(skb, skb->vlan_proto, +					     vlan_tx_tag_get(skb)); +			if (unlikely(!skb)) +				return false; + +			skb_pull(skb, ETH_HLEN); +			skb_reset_mac_len(skb); +			*vid = 0; +			tagged = false; +		} else { +			tagged = true; +		} +	} else { +		/* Untagged frame */ +		tagged = false; +	} + +	if (!*vid) {  		u16 pvid = br_get_pvid(v); -		/* Frame did not have a tag.  See if pvid is set -		 * on this port.  That tells us which vlan untagged -		 * traffic belongs to. +		/* Frame had a tag with VID 0 or did not have a tag. +		 * See if pvid is set on this port.  That tells us which +		 * vlan untagged or priority-tagged traffic belongs to.  		 */  		if (pvid == VLAN_N_VID) -			return false; +			goto drop; -		/* PVID is set on this port.  Any untagged ingress -		 * frame is considered to belong to this vlan. +		/* PVID is set on this port.  Any untagged or priority-tagged +		 * ingress frame is considered to belong to this vlan.  		 */ -		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid); +		*vid = pvid; +		if (likely(!tagged)) +			/* Untagged Frame. */ +			__vlan_hwaccel_put_tag(skb, proto, pvid); +		else +			/* Priority-tagged Frame. +			 * At this point, We know that skb->vlan_tci had +			 * VLAN_TAG_PRESENT bit and its VID field was 0x000. +			 * We update only VID field and preserve PCP field. +			 */ +			skb->vlan_tci |= pvid; +  		return true;  	}  	/* Frame had a valid vlan tag.  See if vlan is allowed */  	if (test_bit(*vid, v->vlan_bitmap))  		return true; - +drop: +	kfree_skb(skb);  	return false;  } @@ -248,7 +266,39 @@ bool br_allowed_egress(struct net_bridge *br,  	return false;  } -/* Must be protected by RTNL */ +/* Called under RCU */ +bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) +{ +	struct net_bridge *br = p->br; +	struct net_port_vlans *v; + +	if (!br->vlan_enabled) +		return true; + +	v = rcu_dereference(p->vlan_info); +	if (!v) +		return false; + +	if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto) +		*vid = 0; + +	if (!*vid) { +		*vid = br_get_pvid(v); +		if (*vid == VLAN_N_VID) +			return false; + +		return true; +	} + +	if (test_bit(*vid, v->vlan_bitmap)) +		return true; + +	return false; +} + +/* Must be protected by RTNL. + * Must be called with vid in range from 1 to 4094 inclusive. + */  int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)  {  	struct net_port_vlans *pv = NULL; @@ -278,7 +328,9 @@ out:  	return err;  } -/* Must be protected by RTNL */ +/* Must be protected by RTNL. + * Must be called with vid in range from 1 to 4094 inclusive. + */  int br_vlan_delete(struct net_bridge *br, u16 vid)  {  	struct net_port_vlans *pv; @@ -289,14 +341,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)  	if (!pv)  		return -EINVAL; -	if (vid) { -		/* If the VID !=0 remove fdb for this vid. VID 0 is special -		 * in that it's the default and is always there in the fdb. -		 */ -		spin_lock_bh(&br->hash_lock); -		fdb_delete_by_addr(br, br->dev->dev_addr, vid); -		spin_unlock_bh(&br->hash_lock); -	} +	br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);  	__vlan_del(pv, vid);  	return 0; @@ -314,6 +359,52 @@ void br_vlan_flush(struct net_bridge *br)  	__vlan_flush(pv);  } +bool br_vlan_find(struct net_bridge *br, u16 vid) +{ +	struct net_port_vlans *pv; +	bool found = false; + +	rcu_read_lock(); +	pv = rcu_dereference(br->vlan_info); + +	if (!pv) +		goto out; + +	if (test_bit(vid, pv->vlan_bitmap)) +		found = true; + +out: +	rcu_read_unlock(); +	return found; +} + +/* Must be protected by RTNL. */ +static void recalculate_group_addr(struct net_bridge *br) +{ +	if (br->group_addr_set) +		return; + +	spin_lock_bh(&br->lock); +	if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) { +		/* Bridge Group Address */ +		br->group_addr[5] = 0x00; +	} else { /* vlan_enabled && ETH_P_8021AD */ +		/* Provider Bridge Group Address */ +		br->group_addr[5] = 0x08; +	} +	spin_unlock_bh(&br->lock); +} + +/* Must be protected by RTNL. */ +void br_recalculate_fwd_mask(struct net_bridge *br) +{ +	if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) +		br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT; +	else /* vlan_enabled && ETH_P_8021AD */ +		br->group_fwd_mask_required = BR_GROUPFWD_8021AD & +					      ~(1u << br->group_addr[5]); +} +  int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)  {  	if (!rtnl_trylock()) @@ -323,13 +414,91 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)  		goto unlock;  	br->vlan_enabled = val; +	br_manage_promisc(br); +	recalculate_group_addr(br); +	br_recalculate_fwd_mask(br);  unlock:  	rtnl_unlock();  	return 0;  } -/* Must be protected by RTNL */ +int br_vlan_set_proto(struct net_bridge *br, unsigned long val) +{ +	int err = 0; +	struct net_bridge_port *p; +	struct net_port_vlans *pv; +	__be16 proto, oldproto; +	u16 vid, errvid; + +	if (val != ETH_P_8021Q && val != ETH_P_8021AD) +		return -EPROTONOSUPPORT; + +	if (!rtnl_trylock()) +		return restart_syscall(); + +	proto = htons(val); +	if (br->vlan_proto == proto) +		goto unlock; + +	/* Add VLANs for the new proto to the device filter. */ +	list_for_each_entry(p, &br->port_list, list) { +		pv = rtnl_dereference(p->vlan_info); +		if (!pv) +			continue; + +		for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { +			err = vlan_vid_add(p->dev, proto, vid); +			if (err) +				goto err_filt; +		} +	} + +	oldproto = br->vlan_proto; +	br->vlan_proto = proto; + +	recalculate_group_addr(br); +	br_recalculate_fwd_mask(br); + +	/* Delete VLANs for the old proto from the device filter. */ +	list_for_each_entry(p, &br->port_list, list) { +		pv = rtnl_dereference(p->vlan_info); +		if (!pv) +			continue; + +		for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) +			vlan_vid_del(p->dev, oldproto, vid); +	} + +unlock: +	rtnl_unlock(); +	return err; + +err_filt: +	errvid = vid; +	for_each_set_bit(vid, pv->vlan_bitmap, errvid) +		vlan_vid_del(p->dev, proto, vid); + +	list_for_each_entry_continue_reverse(p, &br->port_list, list) { +		pv = rtnl_dereference(p->vlan_info); +		if (!pv) +			continue; + +		for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) +			vlan_vid_del(p->dev, proto, vid); +	} + +	goto unlock; +} + +void br_vlan_init(struct net_bridge *br) +{ +	br->vlan_proto = htons(ETH_P_8021Q); +} + +/* Must be protected by RTNL. + * Must be called with vid in range from 1 to 4094 inclusive. + */  int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)  {  	struct net_port_vlans *pv = NULL; @@ -363,7 +532,9 @@ clean_up:  	return err;  } -/* Must be protected by RTNL */ +/* Must be protected by RTNL. + * Must be called with vid in range from 1 to 4094 inclusive. + */  int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)  {  	struct net_port_vlans *pv; @@ -374,14 +545,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)  	if (!pv)  		return -EINVAL; -	if (vid) { -		/* If the VID !=0 remove fdb for this vid. VID 0 is special -		 * in that it's the default and is always there in the fdb. -		 */ -		spin_lock_bh(&port->br->hash_lock); -		fdb_delete_by_addr(port->br, port->dev->dev_addr, vid); -		spin_unlock_bh(&port->br->hash_lock); -	} +	br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);  	return __vlan_del(pv, vid);  } @@ -389,6 +553,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)  void nbp_vlan_flush(struct net_bridge_port *port)  {  	struct net_port_vlans *pv; +	u16 vid;  	ASSERT_RTNL(); @@ -396,6 +561,9 @@ void nbp_vlan_flush(struct net_bridge_port *port)  	if (!pv)  		return; +	for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) +		vlan_vid_del(port->dev, port->br->vlan_proto, vid); +  	__vlan_flush(pv);  } diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index a9aff9c7d02..629dc77874a 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -1,11 +1,24 @@  #  # Bridge netfilter configuration  # +# +menuconfig NF_TABLES_BRIDGE +	depends on BRIDGE && NETFILTER && NF_TABLES +	tristate "Ethernet Bridge nf_tables support" + +if NF_TABLES_BRIDGE + +config NFT_BRIDGE_META +	tristate "Netfilter nf_table bridge meta support" +	depends on NFT_META +	help +	  Add support for bridge dedicated meta key. + +endif # NF_TABLES_BRIDGE  menuconfig BRIDGE_NF_EBTABLES  	tristate "Ethernet Bridge tables (ebtables) support" -	depends on BRIDGE && NETFILTER -	select NETFILTER_XTABLES +	depends on BRIDGE && NETFILTER && NETFILTER_XTABLES  	help  	  ebtables is a general, extensible frame/packet identification  	  framework. Say 'Y' or 'M' here if you want to do Ethernet diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 0718699540b..6f2f3943d66 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -2,6 +2,9 @@  # Makefile for the netfilter modules for Link Layer filtering on a bridge.  # +obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o +obj-$(CONFIG_NFT_BRIDGE_META)  += nft_meta_bridge.o +  obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o  # tables diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 8b84c581be3..9024283d2bc 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -28,7 +28,7 @@ static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,  	uint32_t cmp[2] = { 0, 0 };  	int key = ((const unsigned char *)mac)[5]; -	memcpy(((char *) cmp) + 2, mac, 6); +	ether_addr_copy(((char *) cmp) + 2, mac);  	start = wh->table[key];  	limit = wh->table[key + 1];  	if (ip) { diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index c59f7bfae6e..4e0b0c35932 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -22,7 +22,7 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (!skb_make_writable(skb, 0))  		return EBT_DROP; -	memcpy(eth_hdr(skb)->h_dest, info->mac, ETH_ALEN); +	ether_addr_copy(eth_hdr(skb)->h_dest, info->mac);  	return info->target;  } diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 99c85668f55..17fd5f2cb4b 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -48,10 +48,12 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)  	if (info->bitmask & EBT_IP6_TCLASS &&  	   FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))  		return false; -	if (FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk, -				       &info->saddr), EBT_IP6_SOURCE) || +	if ((info->bitmask & EBT_IP6_SOURCE && +	    FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk, +				       &info->saddr), EBT_IP6_SOURCE)) || +	    (info->bitmask & EBT_IP6_DEST &&  	    FWINV(ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk, -				       &info->daddr), EBT_IP6_DEST)) +				       &info->daddr), EBT_IP6_DEST)))  		return false;  	if (info->bitmask & EBT_IP6_PROTO) {  		uint8_t nexthdr = ih6->nexthdr; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 19c37a4929b..5322a36867a 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -96,7 +96,7 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum,  		bitmask = NF_LOG_MASK;  	if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto == -	   htons(ETH_P_IP)){ +	   htons(ETH_P_IP)) {  		const struct iphdr *ih;  		struct iphdr _iph; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 46624bb6d9b..203964997a5 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -25,10 +25,10 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (par->hooknum != NF_BR_BROUTING)  		/* rcu_read_lock()ed by nf_hook_slow */ -		memcpy(eth_hdr(skb)->h_dest, -		       br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN); +		ether_addr_copy(eth_hdr(skb)->h_dest, +				br_port_get_rcu(par->in)->br->dev->dev_addr);  	else -		memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN); +		ether_addr_copy(eth_hdr(skb)->h_dest, par->in->dev_addr);  	skb->pkt_type = PACKET_HOST;  	return info->target;  } diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index f8f0bd1a1d5..e56ccd060d2 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -24,7 +24,7 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (!skb_make_writable(skb, 0))  		return EBT_DROP; -	memcpy(eth_hdr(skb)->h_source, info->mac, ETH_ALEN); +	ether_addr_copy(eth_hdr(skb)->h_source, info->mac);  	if (!(info->target & NAT_ARP_BIT) &&  	    eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {  		const struct arphdr *ap; @@ -35,7 +35,7 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)  			return EBT_DROP;  		if (ap->ar_hln != ETH_ALEN)  			goto out; -		if (skb_store_bits(skb, sizeof(_ah), info->mac,ETH_ALEN)) +		if (skb_store_bits(skb, sizeof(_ah), info->mac, ETH_ALEN))  			return EBT_DROP;  	}  out: diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 518093802d1..7c470c371e1 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -181,6 +181,7 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  	ub->qlen++;  	pm = nlmsg_data(nlh); +	memset(pm, 0, sizeof(*pm));  	/* Fill in the ulog data */  	pm->version = EBT_ULOG_VERSION; @@ -193,8 +194,6 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  	pm->hook = hooknr;  	if (uloginfo->prefix != NULL)  		strcpy(pm->prefix, uloginfo->prefix); -	else -		*(pm->prefix) = '\0';  	if (in) {  		strcpy(pm->physindev, in->name); @@ -204,16 +203,14 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  			strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);  		else  			strcpy(pm->indev, in->name); -	} else -		pm->indev[0] = pm->physindev[0] = '\0'; +	}  	if (out) {  		/* If out exists, then out is a bridge port */  		strcpy(pm->physoutdev, out->name);  		/* rcu_read_lock()ed by nf_hook_slow */  		strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name); -	} else -		pm->outdev[0] = pm->physoutdev[0] = '\0'; +	}  	if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)  		BUG(); diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index eae67bf0446..8d3f8c7651f 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -14,8 +14,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/if_ether.h> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index dbd1c783431..d2cdf5d6e98 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -23,8 +23,7 @@ static struct ebt_entries initial_chain = {  	.policy		= EBT_ACCEPT,  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "broute",  	.valid_hooks	= 1 << NF_BR_BROUTING,  	.entries_size	= sizeof(struct ebt_entries), @@ -41,8 +40,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static const struct ebt_table broute_table = -{ +static const struct ebt_table broute_table = {  	.name		= "broute",  	.table		= &initial_table,  	.valid_hooks	= 1 << NF_BR_BROUTING, diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 94b2b700cff..ce205aabf9c 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -14,8 +14,7 @@  #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \     (1 << NF_BR_LOCAL_OUT)) -static struct ebt_entries initial_chains[] = -{ +static struct ebt_entries initial_chains[] = {  	{  		.name	= "INPUT",  		.policy	= EBT_ACCEPT, @@ -30,8 +29,7 @@ static struct ebt_entries initial_chains[] =  	},  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "filter",  	.valid_hooks	= FILTER_VALID_HOOKS,  	.entries_size	= 3 * sizeof(struct ebt_entries), @@ -50,8 +48,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static const struct ebt_table frame_filter = -{ +static const struct ebt_table frame_filter = {  	.name		= "filter",  	.table		= &initial_table,  	.valid_hooks	= FILTER_VALID_HOOKS, @@ -60,17 +57,21 @@ static const struct ebt_table frame_filter =  };  static unsigned int -ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, -   const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, +	    const struct net_device *in, const struct net_device *out, +	    int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(in)->xt.frame_filter);  }  static unsigned int -ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, -   const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, +	     const struct net_device *in, const struct net_device *out, +	     int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(out)->xt.frame_filter);  }  static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 322555acdd4..a0ac2984fb6 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -14,8 +14,7 @@  #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \     (1 << NF_BR_POST_ROUTING)) -static struct ebt_entries initial_chains[] = -{ +static struct ebt_entries initial_chains[] = {  	{  		.name	= "PREROUTING",  		.policy	= EBT_ACCEPT, @@ -30,8 +29,7 @@ static struct ebt_entries initial_chains[] =  	}  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "nat",  	.valid_hooks	= NAT_VALID_HOOKS,  	.entries_size	= 3 * sizeof(struct ebt_entries), @@ -50,8 +48,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static struct ebt_table frame_nat = -{ +static struct ebt_table frame_nat = {  	.name		= "nat",  	.table		= &initial_table,  	.valid_hooks	= NAT_VALID_HOOKS, @@ -60,17 +57,21 @@ static struct ebt_table frame_nat =  };  static unsigned int -ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in -   , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, +	   const struct net_device *in, const struct net_device *out, +	   int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(in)->xt.frame_nat);  }  static unsigned int -ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in -   , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, +	    const struct net_device *in, const struct net_device *out, +	    int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(out)->xt.frame_nat);  }  static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index ac780242838..1059ed3bc25 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -118,10 +118,10 @@ ebt_dev_check(const char *entry, const struct net_device *device)  	/* 1 is the wildcard token */  	while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])  		i++; -	return (devname[i] != entry[i] && entry[i] != 1); +	return devname[i] != entry[i] && entry[i] != 1;  } -#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg)) +#define FWINV2(bool, invflg) ((bool) ^ !!(e->invflags & invflg))  /* process standard matches */  static inline int  ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb, @@ -1044,10 +1044,9 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,  	if (repl->num_counters &&  	   copy_to_user(repl->counters, counterstmp,  	   repl->num_counters * sizeof(struct ebt_counter))) { -		ret = -EFAULT; +		/* Silent error, can't fail, new table is already in place */ +		net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");  	} -	else -		ret = 0;  	/* decrease module count and free resources */  	EBT_ENTRY_ITERATE(table->entries, table->entries_size, @@ -1441,7 +1440,7 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,  		return -EFAULT;  	if (*len != sizeof(struct ebt_replace) + entries_size + -	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) +	   (tmp.num_counters ? nentries * sizeof(struct ebt_counter) : 0))  		return -EINVAL;  	if (tmp.nentries != nentries) { @@ -1477,7 +1476,7 @@ static int do_ebt_set_ctl(struct sock *sk,  	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  		return -EPERM; -	switch(cmd) { +	switch (cmd) {  	case EBT_SO_SET_ENTRIES:  		ret = do_replace(net, user, len);  		break; @@ -1507,10 +1506,10 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)  	if (!t)  		return ret; -	switch(cmd) { +	switch (cmd) {  	case EBT_SO_GET_INFO:  	case EBT_SO_GET_INIT_INFO: -		if (*len != sizeof(struct ebt_replace)){ +		if (*len != sizeof(struct ebt_replace)) {  			ret = -EINVAL;  			mutex_unlock(&ebt_mutex);  			break; @@ -1525,7 +1524,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)  			tmp.valid_hooks = t->table->valid_hooks;  		}  		mutex_unlock(&ebt_mutex); -		if (copy_to_user(user, &tmp, *len) != 0){ +		if (copy_to_user(user, &tmp, *len) != 0) {  			BUGPRINT("c2u Didn't work\n");  			ret = -EFAULT;  			break; @@ -2375,8 +2374,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,  }  #endif -static struct nf_sockopt_ops ebt_sockopts = -{ +static struct nf_sockopt_ops ebt_sockopts = {  	.pf		= PF_INET,  	.set_optmin	= EBT_BASE_CTL,  	.set_optmax	= EBT_SO_SET_MAX + 1, diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c new file mode 100644 index 00000000000..5bcc0d8b31f --- /dev/null +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netfilter_bridge.h> +#include <net/netfilter/nf_tables.h> + +static unsigned int +nft_do_chain_bridge(const struct nf_hook_ops *ops, +		    struct sk_buff *skb, +		    const struct net_device *in, +		    const struct net_device *out, +		    int (*okfn)(struct sk_buff *)) +{ +	struct nft_pktinfo pkt; + +	nft_set_pktinfo(&pkt, ops, skb, in, out); + +	return nft_do_chain(&pkt, ops); +} + +static struct nft_af_info nft_af_bridge __read_mostly = { +	.family		= NFPROTO_BRIDGE, +	.nhooks		= NF_BR_NUMHOOKS, +	.owner		= THIS_MODULE, +	.nops		= 1, +	.hooks		= { +		[NF_BR_LOCAL_IN]	= nft_do_chain_bridge, +		[NF_BR_FORWARD]		= nft_do_chain_bridge, +		[NF_BR_LOCAL_OUT]	= nft_do_chain_bridge, +	}, +}; + +static int nf_tables_bridge_init_net(struct net *net) +{ +	net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); +	if (net->nft.bridge == NULL) +		return -ENOMEM; + +	memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge)); + +	if (nft_register_afinfo(net, net->nft.bridge) < 0) +		goto err; + +	return 0; +err: +	kfree(net->nft.bridge); +	return -ENOMEM; +} + +static void nf_tables_bridge_exit_net(struct net *net) +{ +	nft_unregister_afinfo(net->nft.bridge); +	kfree(net->nft.bridge); +} + +static struct pernet_operations nf_tables_bridge_net_ops = { +	.init	= nf_tables_bridge_init_net, +	.exit	= nf_tables_bridge_exit_net, +}; + +static const struct nf_chain_type filter_bridge = { +	.name		= "filter", +	.type		= NFT_CHAIN_T_DEFAULT, +	.family		= NFPROTO_BRIDGE, +	.owner		= THIS_MODULE, +	.hook_mask	= (1 << NF_BR_LOCAL_IN) | +			  (1 << NF_BR_FORWARD) | +			  (1 << NF_BR_LOCAL_OUT), +}; + +static int __init nf_tables_bridge_init(void) +{ +	int ret; + +	nft_register_chain_type(&filter_bridge); +	ret = register_pernet_subsys(&nf_tables_bridge_net_ops); +	if (ret < 0) +		nft_unregister_chain_type(&filter_bridge); + +	return ret; +} + +static void __exit nf_tables_bridge_exit(void) +{ +	unregister_pernet_subsys(&nf_tables_bridge_net_ops); +	nft_unregister_chain_type(&filter_bridge); +} + +module_init(nf_tables_bridge_init); +module_exit(nf_tables_bridge_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); +MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE); diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c new file mode 100644 index 00000000000..4f02109d708 --- /dev/null +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_meta.h> + +#include "../br_private.h" + +static void nft_meta_bridge_get_eval(const struct nft_expr *expr, +				     struct nft_data data[NFT_REG_MAX + 1], +				     const struct nft_pktinfo *pkt) +{ +	const struct nft_meta *priv = nft_expr_priv(expr); +	const struct net_device *in = pkt->in, *out = pkt->out; +	struct nft_data *dest = &data[priv->dreg]; +	const struct net_bridge_port *p; + +	switch (priv->key) { +	case NFT_META_BRI_IIFNAME: +		if (in == NULL || (p = br_port_get_rcu(in)) == NULL) +			goto err; +		break; +	case NFT_META_BRI_OIFNAME: +		if (out == NULL || (p = br_port_get_rcu(out)) == NULL) +			goto err; +		break; +	default: +		goto out; +	} + +	strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data)); +	return; +out: +	return nft_meta_get_eval(expr, data, pkt); +err: +	data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, +				    const struct nft_expr *expr, +				    const struct nlattr * const tb[]) +{ +	struct nft_meta *priv = nft_expr_priv(expr); +	int err; + +	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); +	switch (priv->key) { +	case NFT_META_BRI_IIFNAME: +	case NFT_META_BRI_OIFNAME: +		break; +	default: +		return nft_meta_get_init(ctx, expr, tb); +	} + +	priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); +	err = nft_validate_output_register(priv->dreg); +	if (err < 0) +		return err; + +	err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); +	if (err < 0) +		return err; + +	return 0; +} + +static struct nft_expr_type nft_meta_bridge_type; +static const struct nft_expr_ops nft_meta_bridge_get_ops = { +	.type		= &nft_meta_bridge_type, +	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)), +	.eval		= nft_meta_bridge_get_eval, +	.init		= nft_meta_bridge_get_init, +	.dump		= nft_meta_get_dump, +}; + +static const struct nft_expr_ops nft_meta_bridge_set_ops = { +	.type		= &nft_meta_bridge_type, +	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)), +	.eval		= nft_meta_set_eval, +	.init		= nft_meta_set_init, +	.dump		= nft_meta_set_dump, +}; + +static const struct nft_expr_ops * +nft_meta_bridge_select_ops(const struct nft_ctx *ctx, +			   const struct nlattr * const tb[]) +{ +	if (tb[NFTA_META_KEY] == NULL) +		return ERR_PTR(-EINVAL); + +	if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) +		return ERR_PTR(-EINVAL); + +	if (tb[NFTA_META_DREG]) +		return &nft_meta_bridge_get_ops; + +	if (tb[NFTA_META_SREG]) +		return &nft_meta_bridge_set_ops; + +	return ERR_PTR(-EINVAL); +} + +static struct nft_expr_type nft_meta_bridge_type __read_mostly = { +	.family         = NFPROTO_BRIDGE, +	.name           = "meta", +	.select_ops     = &nft_meta_bridge_select_ops, +	.policy         = nft_meta_policy, +	.maxattr        = NFTA_META_MAX, +	.owner          = THIS_MODULE, +}; + +static int __init nft_meta_bridge_module_init(void) +{ +	return nft_register_expr(&nft_meta_bridge_type); +} + +static void __exit nft_meta_bridge_module_exit(void) +{ +	nft_unregister_expr(&nft_meta_bridge_type); +} + +module_init(nft_meta_bridge_module_init); +module_exit(nft_meta_bridge_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");  | 
