2005-04-17 02:20:36 +04:00
/*
* INET 802.1 Q VLAN
* Ethernet - type device handling .
*
* Authors : Ben Greear < greearb @ candelatech . com >
2008-01-21 11:27:00 +03:00
* Please send support related email to : netdev @ vger . kernel . org
2005-04-17 02:20:36 +04:00
* VLAN Home Page : http : //www.candelatech.com/~greear/vlan.html
2007-02-09 17:24:25 +03:00
*
2005-04-17 02:20:36 +04:00
* Fixes :
* Fix for packet capture - Nick Eggleston < nick @ dccinc . com > ;
* Add HW acceleration hooks - David S . Miller < davem @ redhat . com > ;
* Correct all the locking - David S . Miller < davem @ redhat . com > ;
* Use hash table for VLAN groups - David S . Miller < davem @ redhat . com >
*
* 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 .
*/
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/init.h>
2008-05-12 23:21:05 +04:00
# include <linux/rculist.h>
2005-04-17 02:20:36 +04:00
# include <net/p8022.h>
# include <net/arp.h>
# include <linux/rtnetlink.h>
# include <linux/notifier.h>
2008-07-15 09:51:55 +04:00
# include <net/rtnetlink.h>
2007-09-12 15:02:17 +04:00
# include <net/net_namespace.h>
2008-04-16 11:49:09 +04:00
# include <net/netns/generic.h>
2008-07-15 09:51:55 +04:00
# include <asm/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <linux/if_vlan.h>
# include "vlan.h"
# include "vlanproc.h"
# define DRV_VERSION "1.8"
/* Global VLAN variables */
2009-11-17 13:42:49 +03:00
int vlan_net_id __read_mostly ;
2008-04-16 11:49:09 +04:00
2005-04-17 02:20:36 +04:00
/* Our listing of VLAN group(s) */
static struct hlist_head vlan_group_hash [ VLAN_GRP_HASH_SIZE ] ;
2008-10-29 08:12:36 +03:00
const char vlan_fullname [ ] = " 802.1Q VLAN Support " ;
const char vlan_version [ ] = DRV_VERSION ;
static const char vlan_copyright [ ] = " Ben Greear <greearb@candelatech.com> " ;
static const char vlan_buggyright [ ] = " David S. Miller <davem@redhat.com> " ;
2005-04-17 02:20:36 +04:00
2009-03-09 11:18:29 +03:00
static struct packet_type vlan_packet_type __read_mostly = {
2009-02-01 11:45:17 +03:00
. type = cpu_to_be16 ( ETH_P_8021Q ) ,
2005-04-17 02:20:36 +04:00
. func = vlan_skb_recv , /* VLAN receive method */
} ;
/* End of global variables definitions. */
2008-01-21 11:26:41 +03:00
static inline unsigned int vlan_grp_hashfn ( unsigned int idx )
{
return ( ( idx > > VLAN_GRP_HASH_SHIFT ) ^ idx ) & VLAN_GRP_HASH_MASK ;
}
2005-04-17 02:20:36 +04:00
/* Must be invoked with RCU read lock (no preempt) */
2008-04-16 11:48:04 +04:00
static struct vlan_group * __vlan_find_group ( struct net_device * real_dev )
2005-04-17 02:20:36 +04:00
{
struct vlan_group * grp ;
struct hlist_node * n ;
2008-04-16 11:48:04 +04:00
int hash = vlan_grp_hashfn ( real_dev - > ifindex ) ;
2005-04-17 02:20:36 +04:00
hlist_for_each_entry_rcu ( grp , n , & vlan_group_hash [ hash ] , hlist ) {
2008-04-16 11:48:04 +04:00
if ( grp - > real_dev = = real_dev )
2005-04-17 02:20:36 +04:00
return grp ;
}
return NULL ;
}
/* Find the protocol handler. Assumes VID < VLAN_VID_MASK.
*
* Must be invoked with RCU read lock ( no preempt )
*/
2008-07-08 14:24:44 +04:00
struct net_device * __find_vlan_dev ( struct net_device * real_dev , u16 vlan_id )
2005-04-17 02:20:36 +04:00
{
2008-04-16 11:48:04 +04:00
struct vlan_group * grp = __vlan_find_group ( real_dev ) ;
2005-04-17 02:20:36 +04:00
if ( grp )
2008-07-08 14:24:44 +04:00
return vlan_group_get_device ( grp , vlan_id ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2007-03-03 07:44:51 +03:00
static void vlan_group_free ( struct vlan_group * grp )
{
int i ;
2008-01-21 11:26:41 +03:00
for ( i = 0 ; i < VLAN_GROUP_ARRAY_SPLIT_PARTS ; i + + )
2007-03-03 07:44:51 +03:00
kfree ( grp - > vlan_devices_arrays [ i ] ) ;
kfree ( grp ) ;
}
2008-04-16 11:48:04 +04:00
static struct vlan_group * vlan_group_alloc ( struct net_device * real_dev )
2007-06-13 23:05:59 +04:00
{
struct vlan_group * grp ;
grp = kzalloc ( sizeof ( struct vlan_group ) , GFP_KERNEL ) ;
if ( ! grp )
return NULL ;
2008-04-16 11:48:04 +04:00
grp - > real_dev = real_dev ;
2007-06-13 23:05:59 +04:00
hlist_add_head_rcu ( & grp - > hlist ,
2008-04-16 11:48:04 +04:00
& vlan_group_hash [ vlan_grp_hashfn ( real_dev - > ifindex ) ] ) ;
2007-06-13 23:05:59 +04:00
return grp ;
2008-03-27 02:27:22 +03:00
}
2007-06-13 23:05:59 +04:00
2008-07-08 14:24:44 +04:00
static int vlan_group_prealloc_vid ( struct vlan_group * vg , u16 vlan_id )
2008-03-27 02:27:22 +03:00
{
struct net_device * * array ;
unsigned int size ;
ASSERT_RTNL ( ) ;
2008-07-08 14:24:44 +04:00
array = vg - > vlan_devices_arrays [ vlan_id / VLAN_GROUP_ARRAY_PART_LEN ] ;
2008-03-27 02:27:22 +03:00
if ( array ! = NULL )
return 0 ;
size = sizeof ( struct net_device * ) * VLAN_GROUP_ARRAY_PART_LEN ;
array = kzalloc ( size , GFP_KERNEL ) ;
if ( array = = NULL )
return - ENOBUFS ;
2008-07-08 14:24:44 +04:00
vg - > vlan_devices_arrays [ vlan_id / VLAN_GROUP_ARRAY_PART_LEN ] = array ;
2008-03-27 02:27:22 +03:00
return 0 ;
2007-06-13 23:05:59 +04:00
}
2005-04-17 02:20:36 +04:00
static void vlan_rcu_free ( struct rcu_head * rcu )
{
2007-03-03 07:44:51 +03:00
vlan_group_free ( container_of ( rcu , struct vlan_group , rcu ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-10-27 10:06:36 +03:00
void unregister_vlan_dev ( struct net_device * dev , struct list_head * head )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
2008-01-21 11:25:50 +03:00
struct net_device * real_dev = vlan - > real_dev ;
2008-11-20 08:53:47 +03:00
const struct net_device_ops * ops = real_dev - > netdev_ops ;
2005-04-17 02:20:36 +04:00
struct vlan_group * grp ;
2008-07-08 14:24:44 +04:00
u16 vlan_id = vlan - > vlan_id ;
2005-04-17 02:20:36 +04:00
ASSERT_RTNL ( ) ;
2008-01-21 11:25:31 +03:00
2008-04-16 11:48:04 +04:00
grp = __vlan_find_group ( real_dev ) ;
2008-01-21 11:25:50 +03:00
BUG_ON ( ! grp ) ;
2008-01-21 11:25:31 +03:00
/* Take it out of our own structures, but be sure to interlock with
* HW accelerating devices or SW vlan input packet processing .
*/
if ( real_dev - > features & NETIF_F_HW_VLAN_FILTER )
2008-11-20 08:53:47 +03:00
ops - > ndo_vlan_rx_kill_vid ( real_dev , vlan_id ) ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:25:50 +03:00
grp - > nr_vlans - - ;
2008-01-21 11:25:31 +03:00
2009-10-30 09:43:00 +03:00
vlan_group_set_device ( grp , vlan_id , NULL ) ;
if ( ! grp - > killall )
2009-10-27 10:06:49 +03:00
synchronize_net ( ) ;
2009-10-30 09:43:00 +03:00
2009-10-27 10:06:36 +03:00
unregister_netdevice_queue ( dev , head ) ;
2008-07-06 08:26:41 +04:00
2008-01-21 11:25:31 +03:00
/* If the group is now empty, kill off the group. */
2008-01-21 11:25:50 +03:00
if ( grp - > nr_vlans = = 0 ) {
2008-07-06 08:26:57 +04:00
vlan_gvrp_uninit_applicant ( real_dev ) ;
2008-01-21 11:25:31 +03:00
if ( real_dev - > features & NETIF_F_HW_VLAN_RX )
2008-11-20 08:53:47 +03:00
ops - > ndo_vlan_rx_register ( real_dev , NULL ) ;
2008-01-21 11:25:31 +03:00
hlist_del_rcu ( & grp - > hlist ) ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:25:31 +03:00
/* Free the group, after all cpu's are done. */
call_rcu ( & grp - > rcu , vlan_rcu_free ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:25:50 +03:00
/* Get rid of the vlan's reference to real_dev */
dev_put ( real_dev ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-08 14:24:44 +04:00
int vlan_check_real_dev ( struct net_device * real_dev , u16 vlan_id )
2005-04-17 02:20:36 +04:00
{
2008-11-20 08:53:47 +03:00
const char * name = real_dev - > name ;
const struct net_device_ops * ops = real_dev - > netdev_ops ;
2008-01-21 11:24:30 +03:00
2005-04-17 02:20:36 +04:00
if ( real_dev - > features & NETIF_F_VLAN_CHALLENGED ) {
2008-01-21 11:24:30 +03:00
pr_info ( " 8021q: VLANs not supported on %s \n " , name ) ;
2007-06-13 23:06:14 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2008-11-20 08:53:47 +03:00
if ( ( real_dev - > features & NETIF_F_HW_VLAN_RX ) & & ! ops - > ndo_vlan_rx_register ) {
2008-01-21 11:24:30 +03:00
pr_info ( " 8021q: device %s has buggy VLAN hw accel \n " , name ) ;
2007-06-13 23:06:14 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
if ( ( real_dev - > features & NETIF_F_HW_VLAN_FILTER ) & &
2008-11-20 08:53:47 +03:00
( ! ops - > ndo_vlan_rx_add_vid | | ! ops - > ndo_vlan_rx_kill_vid ) ) {
2008-01-21 11:24:30 +03:00
pr_info ( " 8021q: Device %s has buggy VLAN hw accel \n " , name ) ;
2007-06-13 23:06:14 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:24:30 +03:00
if ( __find_vlan_dev ( real_dev , vlan_id ) ! = NULL )
2007-06-13 23:06:14 +04:00
return - EEXIST ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:06:14 +04:00
return 0 ;
}
2007-06-13 23:07:54 +04:00
int register_vlan_dev ( struct net_device * dev )
2007-06-13 23:06:29 +04:00
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
2007-06-13 23:06:29 +04:00
struct net_device * real_dev = vlan - > real_dev ;
2008-11-20 08:53:47 +03:00
const struct net_device_ops * ops = real_dev - > netdev_ops ;
2008-07-08 14:24:44 +04:00
u16 vlan_id = vlan - > vlan_id ;
2007-06-13 23:06:29 +04:00
struct vlan_group * grp , * ngrp = NULL ;
int err ;
2008-04-16 11:48:04 +04:00
grp = __vlan_find_group ( real_dev ) ;
2007-06-13 23:06:29 +04:00
if ( ! grp ) {
2008-04-16 11:48:04 +04:00
ngrp = grp = vlan_group_alloc ( real_dev ) ;
2007-06-13 23:06:29 +04:00
if ( ! grp )
return - ENOBUFS ;
2008-07-06 08:26:57 +04:00
err = vlan_gvrp_init_applicant ( real_dev ) ;
if ( err < 0 )
goto out_free_group ;
2007-06-13 23:06:29 +04:00
}
2008-03-27 02:27:22 +03:00
err = vlan_group_prealloc_vid ( grp , vlan_id ) ;
if ( err < 0 )
2008-07-06 08:26:57 +04:00
goto out_uninit_applicant ;
2008-03-27 02:27:22 +03:00
2007-06-13 23:06:29 +04:00
err = register_netdevice ( dev ) ;
if ( err < 0 )
2008-07-06 08:26:57 +04:00
goto out_uninit_applicant ;
2007-06-13 23:06:29 +04:00
/* Account for reference in struct vlan_dev_info */
dev_hold ( real_dev ) ;
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( real_dev , dev ) ;
2007-06-13 23:06:29 +04:00
linkwatch_fire_event ( dev ) ; /* _MUST_ call rfc2863_policy() */
/* So, got the sucker initialized, now lets place
* it into our local structure .
*/
vlan_group_set_device ( grp , vlan_id , dev ) ;
2008-01-21 11:25:50 +03:00
grp - > nr_vlans + + ;
2007-06-13 23:06:29 +04:00
if ( ngrp & & real_dev - > features & NETIF_F_HW_VLAN_RX )
2008-11-20 08:53:47 +03:00
ops - > ndo_vlan_rx_register ( real_dev , ngrp ) ;
2007-06-13 23:06:29 +04:00
if ( real_dev - > features & NETIF_F_HW_VLAN_FILTER )
2008-11-20 08:53:47 +03:00
ops - > ndo_vlan_rx_add_vid ( real_dev , vlan_id ) ;
2007-06-13 23:06:29 +04:00
return 0 ;
2008-07-06 08:26:57 +04:00
out_uninit_applicant :
if ( ngrp )
vlan_gvrp_uninit_applicant ( real_dev ) ;
2007-06-13 23:06:29 +04:00
out_free_group :
2009-11-17 17:45:04 +03:00
if ( ngrp ) {
hlist_del_rcu ( & ngrp - > hlist ) ;
/* Free the group, after all cpu's are done. */
call_rcu ( & ngrp - > rcu , vlan_rcu_free ) ;
}
2007-06-13 23:06:29 +04:00
return err ;
}
2007-06-13 23:06:14 +04:00
/* Attach a VLAN device to a mac address (ie Ethernet Card).
2007-06-13 23:06:43 +04:00
* Returns 0 if the device was created or a negative error code otherwise .
2007-06-13 23:06:14 +04:00
*/
2008-07-08 14:24:44 +04:00
static int register_vlan_device ( struct net_device * real_dev , u16 vlan_id )
2007-06-13 23:06:14 +04:00
{
struct net_device * new_dev ;
2008-04-16 11:54:39 +04:00
struct net * net = dev_net ( real_dev ) ;
struct vlan_net * vn = net_generic ( net , vlan_net_id ) ;
2007-06-13 23:06:14 +04:00
char name [ IFNAMSIZ ] ;
2007-06-13 23:06:43 +04:00
int err ;
2007-06-13 23:06:14 +04:00
2008-07-08 14:24:44 +04:00
if ( vlan_id > = VLAN_VID_MASK )
2007-06-13 23:06:43 +04:00
return - ERANGE ;
2007-06-13 23:06:14 +04:00
2008-07-08 14:24:44 +04:00
err = vlan_check_real_dev ( real_dev , vlan_id ) ;
2007-06-13 23:06:43 +04:00
if ( err < 0 )
return err ;
2007-06-13 23:06:14 +04:00
2005-04-17 02:20:36 +04:00
/* Gotta set up the fields for the device. */
2008-04-16 11:54:39 +04:00
switch ( vn - > name_type ) {
2005-04-17 02:20:36 +04:00
case VLAN_NAME_TYPE_RAW_PLUS_VID :
/* name will look like: eth1.0005 */
2008-07-08 14:24:44 +04:00
snprintf ( name , IFNAMSIZ , " %s.%.4i " , real_dev - > name , vlan_id ) ;
2005-04-17 02:20:36 +04:00
break ;
case VLAN_NAME_TYPE_PLUS_VID_NO_PAD :
/* Put our vlan.VID in the name.
* Name will look like : vlan5
*/
2008-07-08 14:24:44 +04:00
snprintf ( name , IFNAMSIZ , " vlan%i " , vlan_id ) ;
2005-04-17 02:20:36 +04:00
break ;
case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
/* Put our vlan.VID in the name.
* Name will look like : eth0 .5
*/
2008-07-08 14:24:44 +04:00
snprintf ( name , IFNAMSIZ , " %s.%i " , real_dev - > name , vlan_id ) ;
2005-04-17 02:20:36 +04:00
break ;
case VLAN_NAME_TYPE_PLUS_VID :
/* Put our vlan.VID in the name.
* Name will look like : vlan0005
*/
default :
2008-07-08 14:24:44 +04:00
snprintf ( name , IFNAMSIZ , " vlan%.4i " , vlan_id ) ;
2007-04-21 04:09:22 +04:00
}
2007-02-09 17:24:25 +03:00
2009-09-03 05:03:00 +04:00
new_dev = alloc_netdev_mq ( sizeof ( struct vlan_dev_info ) , name ,
vlan_setup , real_dev - > num_tx_queues ) ;
2006-07-03 11:25:33 +04:00
2005-04-17 02:20:36 +04:00
if ( new_dev = = NULL )
2007-06-13 23:06:43 +04:00
return - ENOBUFS ;
2005-04-17 02:20:36 +04:00
2009-09-03 05:03:00 +04:00
new_dev - > real_num_tx_queues = real_dev - > real_num_tx_queues ;
2008-04-16 11:55:06 +04:00
dev_net_set ( new_dev , net ) ;
2005-04-17 02:20:36 +04:00
/* need 4 bytes for extra VLAN header info,
* hope the underlying device can handle it .
*/
new_dev - > mtu = real_dev - > mtu ;
2008-07-08 14:24:44 +04:00
vlan_dev_info ( new_dev ) - > vlan_id = vlan_id ;
2008-01-21 11:26:07 +03:00
vlan_dev_info ( new_dev ) - > real_dev = real_dev ;
vlan_dev_info ( new_dev ) - > dent = NULL ;
vlan_dev_info ( new_dev ) - > flags = VLAN_FLAG_REORDER_HDR ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:07:54 +04:00
new_dev - > rtnl_link_ops = & vlan_link_ops ;
2007-06-13 23:06:43 +04:00
err = register_vlan_dev ( new_dev ) ;
if ( err < 0 )
2007-06-13 23:06:29 +04:00
goto out_free_newdev ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:06:43 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
out_free_newdev :
free_netdev ( new_dev ) ;
2007-06-13 23:06:43 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-07-12 06:45:24 +04:00
static void vlan_sync_address ( struct net_device * dev ,
struct net_device * vlandev )
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( vlandev ) ;
2007-07-12 06:45:24 +04:00
/* May be called without an actual change */
if ( ! compare_ether_addr ( vlan - > real_dev_addr , dev - > dev_addr ) )
return ;
/* vlan address was different from the old address and is equal to
* the new address */
if ( compare_ether_addr ( vlandev - > dev_addr , vlan - > real_dev_addr ) & &
! compare_ether_addr ( vlandev - > dev_addr , dev - > dev_addr ) )
2009-05-23 03:22:17 +04:00
dev_unicast_delete ( dev , vlandev - > dev_addr ) ;
2007-07-12 06:45:24 +04:00
/* vlan address was equal to the old address and is different from
* the new address */
if ( ! compare_ether_addr ( vlandev - > dev_addr , vlan - > real_dev_addr ) & &
compare_ether_addr ( vlandev - > dev_addr , dev - > dev_addr ) )
2009-05-23 03:22:17 +04:00
dev_unicast_add ( dev , vlandev - > dev_addr ) ;
2007-07-12 06:45:24 +04:00
memcpy ( vlan - > real_dev_addr , dev - > dev_addr , ETH_ALEN ) ;
}
2008-05-21 01:54:50 +04:00
static void vlan_transfer_features ( struct net_device * dev ,
struct net_device * vlandev )
{
unsigned long old_features = vlandev - > features ;
2008-05-23 11:22:04 +04:00
vlandev - > features & = ~ dev - > vlan_features ;
vlandev - > features | = dev - > features & dev - > vlan_features ;
2008-09-12 07:17:05 +04:00
vlandev - > gso_max_size = dev - > gso_max_size ;
2009-08-14 16:41:07 +04:00
# if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
vlandev - > fcoe_ddp_xid = dev - > fcoe_ddp_xid ;
# endif
2010-03-23 17:42:05 +03:00
vlandev - > real_num_tx_queues = dev - > real_num_tx_queues ;
BUG_ON ( vlandev - > real_num_tx_queues > vlandev - > num_tx_queues ) ;
2008-05-21 01:54:50 +04:00
if ( old_features ! = vlandev - > features )
netdev_features_change ( vlandev ) ;
}
2008-04-02 11:08:01 +04:00
static void __vlan_device_event ( struct net_device * dev , unsigned long event )
{
switch ( event ) {
case NETDEV_CHANGENAME :
vlan_proc_rem_dev ( dev ) ;
if ( vlan_proc_add_dev ( dev ) < 0 )
pr_warning ( " 8021q: failed to change proc name for %s \n " ,
dev - > name ) ;
break ;
2008-04-16 11:57:01 +04:00
case NETDEV_REGISTER :
if ( vlan_proc_add_dev ( dev ) < 0 )
pr_warning ( " 8021q: failed to add proc entry for %s \n " ,
dev - > name ) ;
break ;
case NETDEV_UNREGISTER :
vlan_proc_rem_dev ( dev ) ;
break ;
2008-04-02 11:08:01 +04:00
}
}
2008-01-21 11:26:41 +03:00
static int vlan_device_event ( struct notifier_block * unused , unsigned long event ,
void * ptr )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = ptr ;
2008-04-02 11:08:01 +04:00
struct vlan_group * grp ;
2005-04-17 02:20:36 +04:00
int i , flgs ;
struct net_device * vlandev ;
2009-11-25 10:54:54 +03:00
struct vlan_dev_info * vlan ;
2009-10-30 09:43:00 +03:00
LIST_HEAD ( list ) ;
2005-04-17 02:20:36 +04:00
2008-05-21 01:37:36 +04:00
if ( is_vlan_dev ( dev ) )
2008-04-02 11:08:01 +04:00
__vlan_device_event ( dev , event ) ;
2008-04-16 11:48:04 +04:00
grp = __vlan_find_group ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( ! grp )
goto out ;
/* It is OK that we do not hold the group lock right now,
* as we run under the RTNL lock .
*/
switch ( event ) {
case NETDEV_CHANGE :
/* Propagate real device state to vlan devices */
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
vlandev = vlan_group_get_device ( grp , i ) ;
2005-04-17 02:20:36 +04:00
if ( ! vlandev )
continue ;
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( dev , vlandev ) ;
2005-04-17 02:20:36 +04:00
}
break ;
2007-07-12 06:45:24 +04:00
case NETDEV_CHANGEADDR :
/* Adjust unicast filters on underlying device */
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
vlandev = vlan_group_get_device ( grp , i ) ;
if ( ! vlandev )
continue ;
2007-11-11 08:51:40 +03:00
flgs = vlandev - > flags ;
if ( ! ( flgs & IFF_UP ) )
continue ;
2007-07-12 06:45:24 +04:00
vlan_sync_address ( dev , vlandev ) ;
}
2009-07-20 18:35:37 +04:00
break ;
case NETDEV_CHANGEMTU :
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
vlandev = vlan_group_get_device ( grp , i ) ;
if ( ! vlandev )
continue ;
if ( vlandev - > mtu < = dev - > mtu )
continue ;
dev_set_mtu ( vlandev , dev - > mtu ) ;
}
2007-07-12 06:45:24 +04:00
break ;
2008-05-21 01:54:50 +04:00
case NETDEV_FEAT_CHANGE :
/* Propagate device features to underlying device */
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
vlandev = vlan_group_get_device ( grp , i ) ;
if ( ! vlandev )
continue ;
vlan_transfer_features ( dev , vlandev ) ;
}
break ;
2005-04-17 02:20:36 +04:00
case NETDEV_DOWN :
/* Put all VLANs for this dev in the down state too. */
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
vlandev = vlan_group_get_device ( grp , i ) ;
2005-04-17 02:20:36 +04:00
if ( ! vlandev )
continue ;
flgs = vlandev - > flags ;
if ( ! ( flgs & IFF_UP ) )
continue ;
2009-11-25 10:54:54 +03:00
vlan = vlan_dev_info ( vlandev ) ;
if ( ! ( vlan - > flags & VLAN_FLAG_LOOSE_BINDING ) )
dev_change_flags ( vlandev , flgs & ~ IFF_UP ) ;
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( dev , vlandev ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case NETDEV_UP :
/* Put all VLANs for this dev in the up state too. */
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
vlandev = vlan_group_get_device ( grp , i ) ;
2005-04-17 02:20:36 +04:00
if ( ! vlandev )
continue ;
2007-02-09 17:24:25 +03:00
2005-04-17 02:20:36 +04:00
flgs = vlandev - > flags ;
if ( flgs & IFF_UP )
continue ;
2009-11-25 10:54:54 +03:00
vlan = vlan_dev_info ( vlandev ) ;
if ( ! ( vlan - > flags & VLAN_FLAG_LOOSE_BINDING ) )
dev_change_flags ( vlandev , flgs | IFF_UP ) ;
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( dev , vlandev ) ;
2005-04-17 02:20:36 +04:00
}
break ;
2007-02-09 17:24:25 +03:00
2005-04-17 02:20:36 +04:00
case NETDEV_UNREGISTER :
2009-10-30 09:43:00 +03:00
/* Delete all VLANs for this dev. */
grp - > killall = 1 ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
vlandev = vlan_group_get_device ( grp , i ) ;
if ( ! vlandev )
continue ;
/* unregistration of last vlan destroys group, abort
* afterwards */
if ( grp - > nr_vlans = = 1 )
i = VLAN_GROUP_ARRAY_LEN ;
unregister_vlan_dev ( vlandev , & list ) ;
}
unregister_netdevice_many ( & list ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
out :
return NOTIFY_DONE ;
}
2008-01-21 11:25:15 +03:00
static struct notifier_block vlan_notifier_block __read_mostly = {
. notifier_call = vlan_device_event ,
} ;
2005-04-17 02:20:36 +04:00
/*
* VLAN IOCTL handler .
* o execute requested action or pass command to the device driver
* arg is really a struct vlan_ioctl_args __user * .
*/
2007-09-17 22:56:21 +04:00
static int vlan_ioctl_handler ( struct net * net , void __user * arg )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:05:22 +04:00
int err ;
2005-04-17 02:20:36 +04:00
struct vlan_ioctl_args args ;
2007-06-13 23:05:22 +04:00
struct net_device * dev = NULL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & args , arg , sizeof ( struct vlan_ioctl_args ) ) )
return - EFAULT ;
/* Null terminate this sucker, just in case. */
args . device1 [ 23 ] = 0 ;
args . u . device2 [ 23 ] = 0 ;
2007-06-13 23:05:22 +04:00
rtnl_lock ( ) ;
2005-04-17 02:20:36 +04:00
switch ( args . cmd ) {
case SET_VLAN_INGRESS_PRIORITY_CMD :
2007-06-13 23:05:22 +04:00
case SET_VLAN_EGRESS_PRIORITY_CMD :
case SET_VLAN_FLAG_CMD :
case ADD_VLAN_CMD :
case DEL_VLAN_CMD :
case GET_VLAN_REALDEV_NAME_CMD :
case GET_VLAN_VID_CMD :
err = - ENODEV ;
2008-04-16 11:55:06 +04:00
dev = __dev_get_by_name ( net , args . device1 ) ;
2007-06-13 23:05:22 +04:00
if ( ! dev )
goto out ;
err = - EINVAL ;
2008-07-08 14:22:16 +04:00
if ( args . cmd ! = ADD_VLAN_CMD & & ! is_vlan_dev ( dev ) )
2007-06-13 23:05:22 +04:00
goto out ;
}
switch ( args . cmd ) {
case SET_VLAN_INGRESS_PRIORITY_CMD :
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-06-13 23:05:22 +04:00
break ;
vlan_dev_set_ingress_priority ( dev ,
args . u . skb_priority ,
args . vlan_qos ) ;
2007-11-07 12:31:32 +03:00
err = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case SET_VLAN_EGRESS_PRIORITY_CMD :
2007-06-13 23:05:22 +04:00
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-06-13 23:05:22 +04:00
break ;
err = vlan_dev_set_egress_priority ( dev ,
2005-04-17 02:20:36 +04:00
args . u . skb_priority ,
args . vlan_qos ) ;
break ;
case SET_VLAN_FLAG_CMD :
2007-06-13 23:05:22 +04:00
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-06-13 23:05:22 +04:00
break ;
2008-07-06 08:26:27 +04:00
err = vlan_dev_change_flags ( dev ,
args . vlan_qos ? args . u . flag : 0 ,
args . u . flag ) ;
2005-04-17 02:20:36 +04:00
break ;
case SET_VLAN_NAME_TYPE_CMD :
2007-06-13 23:05:22 +04:00
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-12-07 09:52:16 +03:00
break ;
2007-06-13 23:05:22 +04:00
if ( ( args . u . name_type > = 0 ) & &
( args . u . name_type < VLAN_NAME_TYPE_HIGHEST ) ) {
2008-04-16 11:54:39 +04:00
struct vlan_net * vn ;
vn = net_generic ( net , vlan_net_id ) ;
vn - > name_type = args . u . name_type ;
2005-04-17 02:20:36 +04:00
err = 0 ;
} else {
err = - EINVAL ;
}
break ;
case ADD_VLAN_CMD :
2007-06-13 23:05:22 +04:00
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-06-13 23:05:22 +04:00
break ;
2007-06-13 23:06:43 +04:00
err = register_vlan_device ( dev , args . u . VID ) ;
2005-04-17 02:20:36 +04:00
break ;
case DEL_VLAN_CMD :
2007-06-13 23:05:22 +04:00
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2007-06-13 23:05:22 +04:00
break ;
2009-10-27 10:06:36 +03:00
unregister_vlan_dev ( dev , NULL ) ;
2008-01-21 11:25:50 +03:00
err = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case GET_VLAN_REALDEV_NAME_CMD :
2007-07-25 02:37:11 +04:00
err = 0 ;
2007-06-13 23:05:22 +04:00
vlan_dev_get_realdev_name ( dev , args . u . device2 ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( arg , & args ,
2008-01-21 11:26:41 +03:00
sizeof ( struct vlan_ioctl_args ) ) )
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
break ;
case GET_VLAN_VID_CMD :
2007-07-25 02:37:11 +04:00
err = 0 ;
2008-07-08 14:23:57 +04:00
args . u . VID = vlan_dev_vlan_id ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( arg , & args ,
2008-01-21 11:26:41 +03:00
sizeof ( struct vlan_ioctl_args ) ) )
2007-02-09 17:24:25 +03:00
err = - EFAULT ;
2005-04-17 02:20:36 +04:00
break ;
default :
2008-01-21 11:24:59 +03:00
err = - EOPNOTSUPP ;
2007-06-13 23:05:22 +04:00
break ;
2007-04-21 04:09:22 +04:00
}
2005-12-22 05:39:49 +03:00
out :
2007-06-13 23:05:22 +04:00
rtnl_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2010-01-17 06:35:32 +03:00
static int __net_init vlan_init_net ( struct net * net )
2008-04-16 11:49:09 +04:00
{
2009-11-29 18:46:05 +03:00
struct vlan_net * vn = net_generic ( net , vlan_net_id ) ;
2008-04-16 11:49:09 +04:00
int err ;
2008-04-16 11:54:39 +04:00
vn - > name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD ;
2008-04-16 11:51:12 +04:00
err = vlan_proc_init ( net ) ;
2008-04-16 11:49:09 +04:00
return err ;
}
2010-01-17 06:35:32 +03:00
static void __net_exit vlan_exit_net ( struct net * net )
2008-04-16 11:49:09 +04:00
{
2008-04-16 11:51:12 +04:00
vlan_proc_cleanup ( net ) ;
2008-04-16 11:49:09 +04:00
}
static struct pernet_operations vlan_net_ops = {
. init = vlan_init_net ,
. exit = vlan_exit_net ,
2009-11-29 18:46:05 +03:00
. id = & vlan_net_id ,
. size = sizeof ( struct vlan_net ) ,
2008-04-16 11:49:09 +04:00
} ;
2008-01-21 11:25:15 +03:00
static int __init vlan_proto_init ( void )
{
int err ;
pr_info ( " %s v%s %s \n " , vlan_fullname , vlan_version , vlan_copyright ) ;
pr_info ( " All bugs added by %s \n " , vlan_buggyright ) ;
2009-12-02 16:19:08 +03:00
err = register_pernet_subsys ( & vlan_net_ops ) ;
2008-04-16 11:49:09 +04:00
if ( err < 0 )
goto err0 ;
2008-01-21 11:25:15 +03:00
err = register_netdevice_notifier ( & vlan_notifier_block ) ;
if ( err < 0 )
goto err2 ;
2008-07-06 08:26:57 +04:00
err = vlan_gvrp_init ( ) ;
2008-01-21 11:25:15 +03:00
if ( err < 0 )
goto err3 ;
2008-07-06 08:26:57 +04:00
err = vlan_netlink_init ( ) ;
if ( err < 0 )
goto err4 ;
2008-01-21 11:25:15 +03:00
dev_add_pack ( & vlan_packet_type ) ;
vlan_ioctl_set ( vlan_ioctl_handler ) ;
return 0 ;
2008-07-06 08:26:57 +04:00
err4 :
vlan_gvrp_uninit ( ) ;
2008-01-21 11:25:15 +03:00
err3 :
unregister_netdevice_notifier ( & vlan_notifier_block ) ;
err2 :
2009-12-02 16:19:08 +03:00
unregister_pernet_subsys ( & vlan_net_ops ) ;
2008-04-16 11:49:09 +04:00
err0 :
2008-01-21 11:25:15 +03:00
return err ;
}
static void __exit vlan_cleanup_module ( void )
{
unsigned int i ;
vlan_ioctl_set ( NULL ) ;
vlan_netlink_fini ( ) ;
unregister_netdevice_notifier ( & vlan_notifier_block ) ;
dev_remove_pack ( & vlan_packet_type ) ;
/* This table must be empty if there are no module references left. */
for ( i = 0 ; i < VLAN_GRP_HASH_SIZE ; i + + )
BUG_ON ( ! hlist_empty ( & vlan_group_hash [ i ] ) ) ;
2009-12-02 16:19:08 +03:00
unregister_pernet_subsys ( & vlan_net_ops ) ;
2009-06-08 07:11:28 +04:00
rcu_barrier ( ) ; /* Wait for completion of call_rcu()'s */
2008-07-06 08:26:57 +04:00
vlan_gvrp_uninit ( ) ;
2008-01-21 11:25:15 +03:00
}
module_init ( vlan_proto_init ) ;
module_exit ( vlan_cleanup_module ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;