2008-07-08 03:23:36 -07:00
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/if_vlan.h>
2009-03-01 00:11:52 -08:00
# include <linux/netpoll.h>
2011-07-15 11:47:34 -04:00
# include <linux/export.h>
2008-07-08 03:23:36 -07:00
# include "vlan.h"
2011-10-29 06:13:39 +00:00
bool vlan_do_receive ( struct sk_buff * * skbp , bool last_handler )
2008-07-08 03:23:36 -07:00
{
2010-10-20 13:56:06 +00:00
struct sk_buff * skb = * skbp ;
u16 vlan_id = skb - > vlan_tci & VLAN_VID_MASK ;
vlan_dev: VLAN 0 should be treated as "no vlan tag" (802.1p packet)
- Without the 8021q module loaded in the kernel, all 802.1p packets
(VLAN 0 but QoS tagging) are silently discarded (as expected, as
the protocol is not loaded).
- Without this patch in 8021q module, these packets are forwarded to
the module, but they are discarded also if VLAN 0 is not configured,
which should not be the default behaviour, as VLAN 0 is not really
a VLANed packet but a 802.1p packet. Defining VLAN 0 makes it almost
impossible to communicate with mixed 802.1p and non 802.1p devices on
the same network due to arp table issues.
- Changed logic to skip vlan specific code in vlan_skb_recv if VLAN
is 0 and we have not defined a VLAN with ID 0, but we accept the
packet with the encapsulated proto and pass it later to netif_rx.
- In the vlan device event handler, added some logic to add VLAN 0
to HW filter in devices that support it (this prevented any traffic
in VLAN 0 to reach the stack in e1000e with HW filter under 2.6.35,
and probably also with other HW filtered cards, so we fix it here).
- In the vlan unregister logic, prevent the elimination of VLAN 0
in devices with HW filter.
- The default behaviour is to ignore the VLAN 0 tagging and accept
the packet as if it was not tagged, but we can still define a
VLAN 0 if desired (so it is backwards compatible).
Signed-off-by: Pedro Garcia <pedro.netdev@dondevamos.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-07-18 15:38:44 -07:00
struct net_device * vlan_dev ;
2010-11-10 23:42:00 +00:00
struct vlan_pcpu_stats * rx_stats ;
2008-07-08 03:23:36 -07:00
2010-10-20 13:56:06 +00:00
vlan_dev = vlan_find_dev ( skb - > dev , vlan_id ) ;
if ( ! vlan_dev ) {
2011-10-29 06:13:39 +00:00
/* Only the last call to vlan_do_receive() should change
* pkt_type to PACKET_OTHERHOST
*/
if ( vlan_id & & last_handler )
2010-10-20 13:56:06 +00:00
skb - > pkt_type = PACKET_OTHERHOST ;
return false ;
2010-09-30 02:16:44 +00:00
}
2008-11-04 14:49:57 -08:00
2010-10-20 13:56:06 +00:00
skb = * skbp = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( unlikely ( ! skb ) )
return false ;
2009-01-06 10:50:09 -08:00
2010-10-20 13:56:06 +00:00
skb - > dev = vlan_dev ;
2011-06-10 06:56:58 +00:00
if ( skb - > pkt_type = = PACKET_OTHERHOST ) {
/* Our lower layer thinks this is not local, let's make sure.
* This allows the VLAN to have a different MAC than the
* underlying device , and still route correctly . */
if ( ! compare_ether_addr ( eth_hdr ( skb ) - > h_dest ,
vlan_dev - > dev_addr ) )
skb - > pkt_type = PACKET_HOST ;
}
2011-12-08 04:11:15 +00:00
if ( ! ( vlan_dev_priv ( vlan_dev ) - > flags & VLAN_FLAG_REORDER_HDR ) ) {
2011-06-10 06:56:58 +00:00
unsigned int offset = skb - > data - skb_mac_header ( skb ) ;
/*
* vlan_insert_tag expect skb - > data pointing to mac header .
* So change skb - > data before calling it and change back to
* original position later
*/
skb_push ( skb , offset ) ;
skb = * skbp = vlan_insert_tag ( skb , skb - > vlan_tci ) ;
if ( ! skb )
return false ;
skb_pull ( skb , offset + VLAN_HLEN ) ;
skb_reset_mac_len ( skb ) ;
}
2010-10-20 13:56:06 +00:00
skb - > priority = vlan_get_ingress_priority ( vlan_dev , skb - > vlan_tci ) ;
2008-07-14 22:49:30 -07:00
skb - > vlan_tci = 0 ;
2008-07-08 03:23:36 -07:00
2011-12-08 04:11:15 +00:00
rx_stats = this_cpu_ptr ( vlan_dev_priv ( vlan_dev ) - > vlan_pcpu_stats ) ;
2009-11-17 04:53:09 +00:00
2010-06-24 00:55:06 +00:00
u64_stats_update_begin ( & rx_stats - > syncp ) ;
2009-11-17 04:53:09 +00:00
rx_stats - > rx_packets + + ;
rx_stats - > rx_bytes + = skb - > len ;
2011-06-10 06:56:58 +00:00
if ( skb - > pkt_type = = PACKET_MULTICAST )
2010-06-24 00:55:06 +00:00
rx_stats - > rx_multicast + + ;
u64_stats_update_end ( & rx_stats - > syncp ) ;
2010-10-20 13:56:06 +00:00
return true ;
2008-07-08 03:23:36 -07:00
}
2008-07-08 03:23:57 -07:00
2011-07-20 04:54:05 +00:00
/* Must be invoked with rcu_read_lock or with RTNL. */
struct net_device * __vlan_find_dev_deep ( struct net_device * real_dev ,
u16 vlan_id )
{
2011-12-08 04:11:18 +00:00
struct vlan_info * vlan_info = rcu_dereference_rtnl ( real_dev - > vlan_info ) ;
2011-07-20 04:54:05 +00:00
2011-12-08 04:11:18 +00:00
if ( vlan_info ) {
return vlan_group_get_device ( & vlan_info - > grp , vlan_id ) ;
2011-07-20 04:54:05 +00:00
} else {
/*
* Bonding slaves do not have grp assigned to themselves .
* Grp is assigned to bonding master instead .
*/
if ( netif_is_bond_slave ( real_dev ) )
return __vlan_find_dev_deep ( real_dev - > master , vlan_id ) ;
}
return NULL ;
}
EXPORT_SYMBOL ( __vlan_find_dev_deep ) ;
2008-07-08 03:23:57 -07:00
struct net_device * vlan_dev_real_dev ( const struct net_device * dev )
{
2011-12-08 04:11:15 +00:00
return vlan_dev_priv ( dev ) - > real_dev ;
2008-07-08 03:23:57 -07:00
}
2009-01-26 12:37:53 -08:00
EXPORT_SYMBOL ( vlan_dev_real_dev ) ;
2008-07-08 03:23:57 -07:00
u16 vlan_dev_vlan_id ( const struct net_device * dev )
{
2011-12-08 04:11:15 +00:00
return vlan_dev_priv ( dev ) - > vlan_id ;
2008-07-08 03:23:57 -07:00
}
2009-01-26 12:37:53 -08:00
EXPORT_SYMBOL ( vlan_dev_vlan_id ) ;
2009-01-06 10:50:09 -08:00
2011-06-10 06:56:58 +00:00
static struct sk_buff * vlan_reorder_header ( struct sk_buff * skb )
2011-04-07 19:48:33 +00:00
{
2011-06-10 06:56:58 +00:00
if ( skb_cow ( skb , skb_headroom ( skb ) ) < 0 )
return NULL ;
memmove ( skb - > data - ETH_HLEN , skb - > data - VLAN_ETH_HLEN , 2 * ETH_ALEN ) ;
skb - > mac_header + = VLAN_HLEN ;
skb_reset_mac_len ( skb ) ;
2011-04-07 19:48:33 +00:00
return skb ;
}
struct sk_buff * vlan_untag ( struct sk_buff * skb )
{
struct vlan_hdr * vhdr ;
u16 vlan_tci ;
if ( unlikely ( vlan_tx_tag_present ( skb ) ) ) {
/* vlan_tci is already set-up so leave this for another time */
return skb ;
}
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( unlikely ( ! skb ) )
goto err_free ;
if ( unlikely ( ! pskb_may_pull ( skb , VLAN_HLEN ) ) )
goto err_free ;
vhdr = ( struct vlan_hdr * ) skb - > data ;
vlan_tci = ntohs ( vhdr - > h_vlan_TCI ) ;
__vlan_hwaccel_put_tag ( skb , vlan_tci ) ;
skb_pull_rcsum ( skb , VLAN_HLEN ) ;
vlan_set_encap_proto ( skb , vhdr ) ;
2011-06-10 06:56:58 +00:00
skb = vlan_reorder_header ( skb ) ;
2011-04-07 19:48:33 +00:00
if ( unlikely ( ! skb ) )
goto err_free ;
2011-08-18 21:29:27 -07:00
skb_reset_network_header ( skb ) ;
skb_reset_transport_header ( skb ) ;
2011-04-07 19:48:33 +00:00
return skb ;
err_free :
kfree_skb ( skb ) ;
return NULL ;
}
2011-12-08 04:11:17 +00:00
2011-12-08 04:11:18 +00:00
/*
* vlan info and vid list
*/
static void vlan_group_free ( struct vlan_group * grp )
{
int i ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_SPLIT_PARTS ; i + + )
kfree ( grp - > vlan_devices_arrays [ i ] ) ;
}
static void vlan_info_free ( struct vlan_info * vlan_info )
{
vlan_group_free ( & vlan_info - > grp ) ;
kfree ( vlan_info ) ;
}
static void vlan_info_rcu_free ( struct rcu_head * rcu )
{
vlan_info_free ( container_of ( rcu , struct vlan_info , rcu ) ) ;
}
static struct vlan_info * vlan_info_alloc ( struct net_device * dev )
{
struct vlan_info * vlan_info ;
vlan_info = kzalloc ( sizeof ( struct vlan_info ) , GFP_KERNEL ) ;
if ( ! vlan_info )
return NULL ;
vlan_info - > real_dev = dev ;
INIT_LIST_HEAD ( & vlan_info - > vid_list ) ;
return vlan_info ;
}
struct vlan_vid_info {
struct list_head list ;
unsigned short vid ;
int refcount ;
} ;
static struct vlan_vid_info * vlan_vid_info_get ( struct vlan_info * vlan_info ,
unsigned short vid )
{
struct vlan_vid_info * vid_info ;
list_for_each_entry ( vid_info , & vlan_info - > vid_list , list ) {
if ( vid_info - > vid = = vid )
return vid_info ;
}
return NULL ;
}
static struct vlan_vid_info * vlan_vid_info_alloc ( unsigned short vid )
{
struct vlan_vid_info * vid_info ;
vid_info = kzalloc ( sizeof ( struct vlan_vid_info ) , GFP_KERNEL ) ;
if ( ! vid_info )
return NULL ;
vid_info - > vid = vid ;
return vid_info ;
}
static int __vlan_vid_add ( struct vlan_info * vlan_info , unsigned short vid ,
struct vlan_vid_info * * pvid_info )
2011-12-08 04:11:17 +00:00
{
2011-12-08 04:11:18 +00:00
struct net_device * dev = vlan_info - > real_dev ;
2011-12-08 04:11:17 +00:00
const struct net_device_ops * ops = dev - > netdev_ops ;
2011-12-08 04:11:18 +00:00
struct vlan_vid_info * vid_info ;
int err ;
vid_info = vlan_vid_info_alloc ( vid ) ;
if ( ! vid_info )
return - ENOMEM ;
2011-12-08 04:11:17 +00:00
if ( ( dev - > features & NETIF_F_HW_VLAN_FILTER ) & &
2011-12-08 04:11:18 +00:00
ops - > ndo_vlan_rx_add_vid ) {
err = ops - > ndo_vlan_rx_add_vid ( dev , vid ) ;
if ( err ) {
kfree ( vid_info ) ;
return err ;
}
2011-12-08 04:11:17 +00:00
}
2011-12-08 04:11:18 +00:00
list_add ( & vid_info - > list , & vlan_info - > vid_list ) ;
vlan_info - > nr_vids + + ;
* pvid_info = vid_info ;
2011-12-08 04:11:17 +00:00
return 0 ;
}
2011-12-08 04:11:18 +00:00
int vlan_vid_add ( struct net_device * dev , unsigned short vid )
{
struct vlan_info * vlan_info ;
struct vlan_vid_info * vid_info ;
bool vlan_info_created = false ;
int err ;
ASSERT_RTNL ( ) ;
vlan_info = rtnl_dereference ( dev - > vlan_info ) ;
if ( ! vlan_info ) {
vlan_info = vlan_info_alloc ( dev ) ;
if ( ! vlan_info )
return - ENOMEM ;
vlan_info_created = true ;
}
vid_info = vlan_vid_info_get ( vlan_info , vid ) ;
if ( ! vid_info ) {
err = __vlan_vid_add ( vlan_info , vid , & vid_info ) ;
if ( err )
goto out_free_vlan_info ;
}
vid_info - > refcount + + ;
if ( vlan_info_created )
rcu_assign_pointer ( dev - > vlan_info , vlan_info ) ;
return 0 ;
out_free_vlan_info :
if ( vlan_info_created )
kfree ( vlan_info ) ;
return err ;
}
2011-12-08 04:11:17 +00:00
EXPORT_SYMBOL ( vlan_vid_add ) ;
2011-12-08 04:11:18 +00:00
static void __vlan_vid_del ( struct vlan_info * vlan_info ,
struct vlan_vid_info * vid_info )
2011-12-08 04:11:17 +00:00
{
2011-12-08 04:11:18 +00:00
struct net_device * dev = vlan_info - > real_dev ;
2011-12-08 04:11:17 +00:00
const struct net_device_ops * ops = dev - > netdev_ops ;
2011-12-08 04:11:18 +00:00
unsigned short vid = vid_info - > vid ;
int err ;
2011-12-08 04:11:17 +00:00
if ( ( dev - > features & NETIF_F_HW_VLAN_FILTER ) & &
ops - > ndo_vlan_rx_kill_vid ) {
2011-12-08 04:11:18 +00:00
err = ops - > ndo_vlan_rx_kill_vid ( dev , vid ) ;
if ( err ) {
pr_warn ( " failed to kill vid %d for device %s \n " ,
vid , dev - > name ) ;
}
}
list_del ( & vid_info - > list ) ;
kfree ( vid_info ) ;
vlan_info - > nr_vids - - ;
}
void vlan_vid_del ( struct net_device * dev , unsigned short vid )
{
struct vlan_info * vlan_info ;
struct vlan_vid_info * vid_info ;
ASSERT_RTNL ( ) ;
vlan_info = rtnl_dereference ( dev - > vlan_info ) ;
if ( ! vlan_info )
return ;
vid_info = vlan_vid_info_get ( vlan_info , vid ) ;
if ( ! vid_info )
return ;
vid_info - > refcount - - ;
if ( vid_info - > refcount = = 0 ) {
__vlan_vid_del ( vlan_info , vid_info ) ;
if ( vlan_info - > nr_vids = = 0 ) {
RCU_INIT_POINTER ( dev - > vlan_info , NULL ) ;
call_rcu ( & vlan_info - > rcu , vlan_info_rcu_free ) ;
}
2011-12-08 04:11:17 +00:00
}
}
EXPORT_SYMBOL ( vlan_vid_del ) ;
2011-12-08 04:11:19 +00:00
int vlan_vids_add_by_dev ( struct net_device * dev ,
const struct net_device * by_dev )
{
struct vlan_vid_info * vid_info ;
2011-12-13 20:29:43 +00:00
struct vlan_info * vlan_info ;
2011-12-08 04:11:19 +00:00
int err ;
ASSERT_RTNL ( ) ;
2011-12-13 20:29:43 +00:00
vlan_info = rtnl_dereference ( by_dev - > vlan_info ) ;
if ( ! vlan_info )
2011-12-08 04:11:19 +00:00
return 0 ;
2011-12-13 20:29:43 +00:00
list_for_each_entry ( vid_info , & vlan_info - > vid_list , list ) {
2011-12-08 04:11:19 +00:00
err = vlan_vid_add ( dev , vid_info - > vid ) ;
if ( err )
goto unwind ;
}
return 0 ;
unwind :
list_for_each_entry_continue_reverse ( vid_info ,
2011-12-13 20:29:43 +00:00
& vlan_info - > vid_list ,
2011-12-08 04:11:19 +00:00
list ) {
vlan_vid_del ( dev , vid_info - > vid ) ;
}
return err ;
}
EXPORT_SYMBOL ( vlan_vids_add_by_dev ) ;
void vlan_vids_del_by_dev ( struct net_device * dev ,
const struct net_device * by_dev )
{
struct vlan_vid_info * vid_info ;
2011-12-13 20:29:43 +00:00
struct vlan_info * vlan_info ;
2011-12-08 04:11:19 +00:00
ASSERT_RTNL ( ) ;
2011-12-13 20:29:43 +00:00
vlan_info = rtnl_dereference ( by_dev - > vlan_info ) ;
if ( ! vlan_info )
2011-12-08 04:11:19 +00:00
return ;
2011-12-13 20:29:43 +00:00
list_for_each_entry ( vid_info , & vlan_info - > vid_list , list )
2011-12-08 04:11:19 +00:00
vlan_vid_del ( dev , vid_info - > vid ) ;
}
EXPORT_SYMBOL ( vlan_vids_del_by_dev ) ;