2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-02-11 03:03:46 +03:00
# include <linux/etherdevice.h>
# include <linux/if_macvlan.h>
2017-02-11 03:03:47 +03:00
# include <linux/if_tap.h>
2017-02-11 03:03:46 +03:00
# include <linux/if_vlan.h>
# include <linux/interrupt.h>
# include <linux/nsproxy.h>
# include <linux/compat.h>
# include <linux/if_tun.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/cache.h>
2017-02-02 21:15:33 +03:00
# include <linux/sched/signal.h>
2017-02-11 03:03:46 +03:00
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/wait.h>
# include <linux/cdev.h>
# include <linux/idr.h>
# include <linux/fs.h>
# include <linux/uio.h>
# include <net/net_namespace.h>
# include <net/rtnetlink.h>
# include <net/sock.h>
# include <linux/virtio_net.h>
# include <linux/skb_array.h>
2017-02-11 03:03:49 +03:00
struct macvtap_dev {
struct macvlan_dev vlan ;
struct tap_dev tap ;
} ;
2017-02-11 03:03:46 +03:00
/*
* Variables for dealing with macvtaps device numbers .
*/
static dev_t macvtap_major ;
static const void * macvtap_net_namespace ( struct device * d )
{
struct net_device * dev = to_net_dev ( d - > parent ) ;
return dev_net ( dev ) ;
}
static struct class macvtap_class = {
. name = " macvtap " ,
. owner = THIS_MODULE ,
. ns_type = & net_ns_type_operations ,
. namespace = macvtap_net_namespace ,
} ;
static struct cdev macvtap_cdev ;
# define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
2017-07-03 16:33:08 +03:00
NETIF_F_TSO6 )
2017-02-11 03:03:46 +03:00
2017-02-11 03:03:49 +03:00
static void macvtap_count_tx_dropped ( struct tap_dev * tap )
{
struct macvtap_dev * vlantap = container_of ( tap , struct macvtap_dev , tap ) ;
struct macvlan_dev * vlan = & vlantap - > vlan ;
this_cpu_inc ( vlan - > pcpu_stats - > tx_dropped ) ;
}
static void macvtap_count_rx_dropped ( struct tap_dev * tap )
{
struct macvtap_dev * vlantap = container_of ( tap , struct macvtap_dev , tap ) ;
struct macvlan_dev * vlan = & vlantap - > vlan ;
macvlan_count_rx ( vlan , 0 , 0 , 0 ) ;
}
static void macvtap_update_features ( struct tap_dev * tap ,
netdev_features_t features )
{
struct macvtap_dev * vlantap = container_of ( tap , struct macvtap_dev , tap ) ;
struct macvlan_dev * vlan = & vlantap - > vlan ;
vlan - > set_features = features ;
netdev_update_features ( vlan - > dev ) ;
}
2017-06-26 00:55:59 +03:00
static int macvtap_newlink ( struct net * src_net , struct net_device * dev ,
struct nlattr * tb [ ] , struct nlattr * data [ ] ,
struct netlink_ext_ack * extack )
2017-02-11 03:03:46 +03:00
{
2017-02-11 03:03:49 +03:00
struct macvtap_dev * vlantap = netdev_priv ( dev ) ;
2017-02-11 03:03:46 +03:00
int err ;
2017-02-11 03:03:49 +03:00
INIT_LIST_HEAD ( & vlantap - > tap . queue_list ) ;
2017-02-11 03:03:46 +03:00
/* Since macvlan supports all offloads by default, make
* tap support all offloads also .
*/
2017-02-11 03:03:49 +03:00
vlantap - > tap . tap_features = TUN_OFFLOADS ;
2017-02-11 03:03:46 +03:00
2017-02-11 03:03:49 +03:00
/* Register callbacks for rx/tx drops accounting and updating
* net_device features
*/
vlantap - > tap . count_tx_dropped = macvtap_count_tx_dropped ;
vlantap - > tap . count_rx_dropped = macvtap_count_rx_dropped ;
vlantap - > tap . update_features = macvtap_update_features ;
err = netdev_rx_handler_register ( dev , tap_handle_frame , & vlantap - > tap ) ;
2017-02-11 03:03:46 +03:00
if ( err )
return err ;
/* Don't put anything that may fail after macvlan_common_newlink
* because we can ' t undo what it does .
*/
2017-10-05 03:48:47 +03:00
err = macvlan_common_newlink ( src_net , dev , tb , data , extack ) ;
2017-02-11 03:03:46 +03:00
if ( err ) {
netdev_rx_handler_unregister ( dev ) ;
return err ;
}
2017-02-11 03:03:49 +03:00
vlantap - > tap . dev = vlantap - > vlan . dev ;
2017-02-11 03:03:46 +03:00
return 0 ;
}
static void macvtap_dellink ( struct net_device * dev ,
struct list_head * head )
{
2017-02-11 03:03:49 +03:00
struct macvtap_dev * vlantap = netdev_priv ( dev ) ;
2017-02-11 03:03:46 +03:00
netdev_rx_handler_unregister ( dev ) ;
2017-02-11 03:03:49 +03:00
tap_del_queues ( & vlantap - > tap ) ;
2017-02-11 03:03:46 +03:00
macvlan_dellink ( dev , head ) ;
}
static void macvtap_setup ( struct net_device * dev )
{
macvlan_common_setup ( dev ) ;
dev - > tx_queue_len = TUN_READQ_SIZE ;
}
static struct rtnl_link_ops macvtap_link_ops __read_mostly = {
. kind = " macvtap " ,
. setup = macvtap_setup ,
. newlink = macvtap_newlink ,
. dellink = macvtap_dellink ,
2017-02-11 03:03:49 +03:00
. priv_size = sizeof ( struct macvtap_dev ) ,
2017-02-11 03:03:46 +03:00
} ;
static int macvtap_device_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
{
struct net_device * dev = netdev_notifier_info_to_dev ( ptr ) ;
2017-02-11 03:03:49 +03:00
struct macvtap_dev * vlantap ;
2017-02-11 03:03:46 +03:00
struct device * classdev ;
dev_t devt ;
int err ;
char tap_name [ IFNAMSIZ ] ;
if ( dev - > rtnl_link_ops ! = & macvtap_link_ops )
return NOTIFY_DONE ;
snprintf ( tap_name , IFNAMSIZ , " tap%d " , dev - > ifindex ) ;
2017-02-11 03:03:49 +03:00
vlantap = netdev_priv ( dev ) ;
2017-02-11 03:03:46 +03:00
switch ( event ) {
case NETDEV_REGISTER :
/* Create the device node here after the network device has
* been registered but before register_netdevice has
* finished running .
*/
2017-02-11 03:03:50 +03:00
err = tap_get_minor ( macvtap_major , & vlantap - > tap ) ;
2017-02-11 03:03:46 +03:00
if ( err )
return notifier_from_errno ( err ) ;
2017-02-11 03:03:49 +03:00
devt = MKDEV ( MAJOR ( macvtap_major ) , vlantap - > tap . minor ) ;
2017-02-11 03:03:46 +03:00
classdev = device_create ( & macvtap_class , & dev - > dev , devt ,
dev , tap_name ) ;
if ( IS_ERR ( classdev ) ) {
2017-02-11 03:03:50 +03:00
tap_free_minor ( macvtap_major , & vlantap - > tap ) ;
2017-02-11 03:03:46 +03:00
return notifier_from_errno ( PTR_ERR ( classdev ) ) ;
}
err = sysfs_create_link ( & dev - > dev . kobj , & classdev - > kobj ,
tap_name ) ;
if ( err )
return notifier_from_errno ( err ) ;
break ;
case NETDEV_UNREGISTER :
/* vlan->minor == 0 if NETDEV_REGISTER above failed */
2017-02-11 03:03:49 +03:00
if ( vlantap - > tap . minor = = 0 )
2017-02-11 03:03:46 +03:00
break ;
sysfs_remove_link ( & dev - > dev . kobj , tap_name ) ;
2017-02-11 03:03:49 +03:00
devt = MKDEV ( MAJOR ( macvtap_major ) , vlantap - > tap . minor ) ;
2017-02-11 03:03:46 +03:00
device_destroy ( & macvtap_class , devt ) ;
2017-02-11 03:03:50 +03:00
tap_free_minor ( macvtap_major , & vlantap - > tap ) ;
2017-02-11 03:03:46 +03:00
break ;
case NETDEV_CHANGE_TX_QUEUE_LEN :
2017-02-11 03:03:49 +03:00
if ( tap_queue_resize ( & vlantap - > tap ) )
2017-02-11 03:03:46 +03:00
return NOTIFY_BAD ;
break ;
}
return NOTIFY_DONE ;
}
static struct notifier_block macvtap_notifier_block __read_mostly = {
. notifier_call = macvtap_device_event ,
} ;
static int macvtap_init ( void )
{
int err ;
2017-10-27 10:00:16 +03:00
err = tap_create_cdev ( & macvtap_cdev , & macvtap_major , " macvtap " ,
THIS_MODULE ) ;
2017-02-11 03:03:46 +03:00
if ( err )
2017-02-11 03:03:48 +03:00
goto out1 ;
2017-02-11 03:03:46 +03:00
err = class_register ( & macvtap_class ) ;
if ( err )
2017-02-11 03:03:48 +03:00
goto out2 ;
2017-02-11 03:03:46 +03:00
err = register_netdevice_notifier ( & macvtap_notifier_block ) ;
if ( err )
2017-02-11 03:03:48 +03:00
goto out3 ;
2017-02-11 03:03:46 +03:00
err = macvlan_link_register ( & macvtap_link_ops ) ;
if ( err )
2017-02-11 03:03:48 +03:00
goto out4 ;
2017-02-11 03:03:46 +03:00
return 0 ;
out4 :
2017-02-11 03:03:48 +03:00
unregister_netdevice_notifier ( & macvtap_notifier_block ) ;
2017-02-11 03:03:46 +03:00
out3 :
2017-02-11 03:03:48 +03:00
class_unregister ( & macvtap_class ) ;
2017-02-11 03:03:46 +03:00
out2 :
2017-02-11 03:03:48 +03:00
tap_destroy_cdev ( macvtap_major , & macvtap_cdev ) ;
2017-02-11 03:03:46 +03:00
out1 :
return err ;
}
module_init ( macvtap_init ) ;
static void macvtap_exit ( void )
{
rtnl_link_unregister ( & macvtap_link_ops ) ;
unregister_netdevice_notifier ( & macvtap_notifier_block ) ;
class_unregister ( & macvtap_class ) ;
2017-02-11 03:03:48 +03:00
tap_destroy_cdev ( macvtap_major , & macvtap_cdev ) ;
2017-02-11 03:03:46 +03:00
}
module_exit ( macvtap_exit ) ;
MODULE_ALIAS_RTNL_LINK ( " macvtap " ) ;
MODULE_AUTHOR ( " Arnd Bergmann <arnd@arndb.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;