forked from mirrors/linux
		
	bridge: Prepare for 802.1ad vlan filtering support
This enables a bridge to have vlan protocol informantion and allows vlan tag manipulation (retrieve, insert and remove tags) according to the vlan protocol. Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									1c5abb6c77
								
							
						
					
					
						commit
						8580e2117c
					
				
					 3 changed files with 51 additions and 12 deletions
				
			
		| 
						 | 
					@ -388,4 +388,5 @@ void br_dev_setup(struct net_device *dev)
 | 
				
			||||||
	br_netfilter_rtable_init(br);
 | 
						br_netfilter_rtable_init(br);
 | 
				
			||||||
	br_stp_timer_init(br);
 | 
						br_stp_timer_init(br);
 | 
				
			||||||
	br_multicast_init(br);
 | 
						br_multicast_init(br);
 | 
				
			||||||
 | 
						br_vlan_init(br);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,6 +294,7 @@ struct net_bridge
 | 
				
			||||||
	u32				auto_cnt;
 | 
						u32				auto_cnt;
 | 
				
			||||||
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
 | 
					#ifdef CONFIG_BRIDGE_VLAN_FILTERING
 | 
				
			||||||
	u8				vlan_enabled;
 | 
						u8				vlan_enabled;
 | 
				
			||||||
 | 
						__be16				vlan_proto;
 | 
				
			||||||
	struct net_port_vlans __rcu	*vlan_info;
 | 
						struct net_port_vlans __rcu	*vlan_info;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -594,6 +595,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid);
 | 
				
			||||||
void br_vlan_flush(struct net_bridge *br);
 | 
					void br_vlan_flush(struct net_bridge *br);
 | 
				
			||||||
bool br_vlan_find(struct net_bridge *br, u16 vid);
 | 
					bool br_vlan_find(struct net_bridge *br, u16 vid);
 | 
				
			||||||
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
 | 
					int br_vlan_filter_toggle(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_add(struct net_bridge_port *port, u16 vid, u16 flags);
 | 
				
			||||||
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 | 
					int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 | 
				
			||||||
void nbp_vlan_flush(struct net_bridge_port *port);
 | 
					void nbp_vlan_flush(struct net_bridge_port *port);
 | 
				
			||||||
| 
						 | 
					@ -689,6 +691,10 @@ static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
					static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return -EOPNOTSUPP;
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 | 
				
			||||||
		 * that ever changes this code will allow tagged
 | 
							 * that ever changes this code will allow tagged
 | 
				
			||||||
		 * traffic to enter the bridge.
 | 
							 * traffic to enter the bridge.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid);
 | 
							err = vlan_vid_add(dev, br->vlan_proto, vid);
 | 
				
			||||||
		if (err)
 | 
							if (err)
 | 
				
			||||||
			return err;
 | 
								return err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_filt:
 | 
					out_filt:
 | 
				
			||||||
	if (p)
 | 
						if (p)
 | 
				
			||||||
		vlan_vid_del(dev, htons(ETH_P_8021Q), vid);
 | 
							vlan_vid_del(dev, br->vlan_proto, vid);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,8 +92,10 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 | 
				
			||||||
	__vlan_delete_pvid(v, vid);
 | 
						__vlan_delete_pvid(v, vid);
 | 
				
			||||||
	clear_bit(vid, v->untagged_bitmap);
 | 
						clear_bit(vid, v->untagged_bitmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (v->port_idx)
 | 
						if (v->port_idx) {
 | 
				
			||||||
		vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid);
 | 
							struct net_bridge_port *p = v->parent.port;
 | 
				
			||||||
 | 
							vlan_vid_del(p->dev, p->br->vlan_proto, vid);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clear_bit(vid, v->vlan_bitmap);
 | 
						clear_bit(vid, v->vlan_bitmap);
 | 
				
			||||||
	v->num_vlans--;
 | 
						v->num_vlans--;
 | 
				
			||||||
| 
						 | 
					@ -158,7 +160,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 | 
				
			||||||
bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 | 
					bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 | 
				
			||||||
			struct sk_buff *skb, u16 *vid)
 | 
								struct sk_buff *skb, u16 *vid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err;
 | 
						bool tagged;
 | 
				
			||||||
 | 
						__be16 proto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If VLAN filtering is disabled on the bridge, all packets are
 | 
						/* If VLAN filtering is disabled on the bridge, all packets are
 | 
				
			||||||
	 * permitted.
 | 
						 * permitted.
 | 
				
			||||||
| 
						 | 
					@ -172,19 +175,41 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 | 
				
			||||||
	if (!v)
 | 
						if (!v)
 | 
				
			||||||
		goto drop;
 | 
							goto drop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proto = br->vlan_proto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If vlan tx offload is disabled on bridge device and frame was
 | 
						/* If vlan tx offload is disabled on bridge device and frame was
 | 
				
			||||||
	 * sent from vlan device on the bridge device, it does not have
 | 
						 * sent from vlan device on the bridge device, it does not have
 | 
				
			||||||
	 * HW accelerated vlan tag.
 | 
						 * HW accelerated vlan tag.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (unlikely(!vlan_tx_tag_present(skb) &&
 | 
						if (unlikely(!vlan_tx_tag_present(skb) &&
 | 
				
			||||||
		     (skb->protocol == htons(ETH_P_8021Q) ||
 | 
							     skb->protocol == proto)) {
 | 
				
			||||||
		      skb->protocol == htons(ETH_P_8021AD)))) {
 | 
					 | 
				
			||||||
		skb = vlan_untag(skb);
 | 
							skb = vlan_untag(skb);
 | 
				
			||||||
		if (unlikely(!skb))
 | 
							if (unlikely(!skb))
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = br_vlan_get_tag(skb, vid);
 | 
						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) {
 | 
						if (!*vid) {
 | 
				
			||||||
		u16 pvid = br_get_pvid(v);
 | 
							u16 pvid = br_get_pvid(v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,9 +224,9 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
 | 
				
			||||||
		 * ingress frame is considered to belong to this vlan.
 | 
							 * ingress frame is considered to belong to this vlan.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		*vid = pvid;
 | 
							*vid = pvid;
 | 
				
			||||||
		if (likely(err))
 | 
							if (likely(!tagged))
 | 
				
			||||||
			/* Untagged Frame. */
 | 
								/* Untagged Frame. */
 | 
				
			||||||
			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
 | 
								__vlan_hwaccel_put_tag(skb, proto, pvid);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			/* Priority-tagged Frame.
 | 
								/* Priority-tagged Frame.
 | 
				
			||||||
			 * At this point, We know that skb->vlan_tci had
 | 
								 * At this point, We know that skb->vlan_tci had
 | 
				
			||||||
| 
						 | 
					@ -254,7 +279,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
 | 
				
			||||||
	if (!v)
 | 
						if (!v)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	br_vlan_get_tag(skb, vid);
 | 
						if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
 | 
				
			||||||
 | 
							*vid = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*vid) {
 | 
						if (!*vid) {
 | 
				
			||||||
		*vid = br_get_pvid(v);
 | 
							*vid = br_get_pvid(v);
 | 
				
			||||||
		if (*vid == VLAN_N_VID)
 | 
							if (*vid == VLAN_N_VID)
 | 
				
			||||||
| 
						 | 
					@ -367,6 +394,11 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void br_vlan_init(struct net_bridge *br)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						br->vlan_proto = htons(ETH_P_8021Q);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Must be protected by RTNL.
 | 
					/* Must be protected by RTNL.
 | 
				
			||||||
 * Must be called with vid in range from 1 to 4094 inclusive.
 | 
					 * Must be called with vid in range from 1 to 4094 inclusive.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -433,7 +465,7 @@ void nbp_vlan_flush(struct net_bridge_port *port)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
 | 
						for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
 | 
				
			||||||
		vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid);
 | 
							vlan_vid_del(port->dev, port->br->vlan_proto, vid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__vlan_flush(pv);
 | 
						__vlan_flush(pv);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue