2005-04-16 15:20:36 -07:00
/* dummy.c: a dummy net driver
The purpose of this driver is to provide a device to point a
route through , but not to actually transmit packets .
Why ? If you have a machine whose only connection is an occasional
PPP / SLIP / PLIP link , you can only connect to your own hostname
when the link is up . Otherwise you have to use localhost .
This isn ' t very consistent .
One solution is to set up a dummy link using PPP / SLIP / PLIP ,
but this seems ( to me ) too much overhead for too little gain .
This driver provides a small alternative . Thus you can do
2006-09-13 13:24:59 -04:00
2005-04-16 15:20:36 -07:00
[ when not running slip ]
ifconfig dummy slip . addr . ess . here up
[ to go to slip ]
ifconfig dummy down
dip whatever
This was written by looking at Donald Becker ' s skeleton driver
and the loopback driver . I then threw away anything that didn ' t
apply ! Thanks to Alan Cox for the key clue on what to do with
misguided packets .
Nick Holloway , 27 th May 1994
[ I tweaked this explanation a little but that ' s all ]
Alan Cox , 30 th May 1994
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/init.h>
# include <linux/moduleparam.h>
2007-06-13 12:04:20 -07:00
# include <linux/rtnetlink.h>
2007-06-13 12:04:34 -07:00
# include <net/rtnetlink.h>
2010-09-27 20:50:33 +00:00
# include <linux/u64_stats_sync.h>
2007-06-13 12:04:20 -07:00
2014-12-05 22:13:24 -02:00
# define DRV_NAME "dummy"
# define DRV_VERSION "1.0"
2017-01-23 12:17:33 +01:00
# undef pr_fmt
# define pr_fmt(fmt) DRV_NAME ": " fmt
2005-04-16 15:20:36 -07:00
static int numdummies = 1 ;
2017-01-23 12:17:33 +01:00
static int num_vfs ;
struct vf_data_storage {
u8 vf_mac [ ETH_ALEN ] ;
u16 pf_vlan ; /* When set, guest VLAN config not allowed. */
u16 pf_qos ;
__be16 vlan_proto ;
u16 min_tx_rate ;
u16 max_tx_rate ;
u8 spoofchk_enabled ;
bool rss_query_enabled ;
u8 trusted ;
int link_state ;
} ;
struct dummy_priv {
struct vf_data_storage * vfinfo ;
} ;
static int dummy_num_vf ( struct device * dev )
{
return num_vfs ;
}
static struct bus_type dummy_bus = {
. name = " dummy " ,
. num_vf = dummy_num_vf ,
} ;
static void release_dummy_parent ( struct device * dev )
{
}
static struct device dummy_parent = {
. init_name = " dummy " ,
. bus = & dummy_bus ,
. release = release_dummy_parent ,
} ;
2005-04-16 15:20:36 -07:00
/* fake multicast ability */
static void set_multicast_list ( struct net_device * dev )
{
}
2010-09-27 20:50:33 +00:00
struct pcpu_dstats {
u64 tx_packets ;
u64 tx_bytes ;
struct u64_stats_sync syncp ;
} ;
2017-01-06 19:12:52 -08:00
static void dummy_get_stats64 ( struct net_device * dev ,
struct rtnl_link_stats64 * stats )
2010-09-27 20:50:33 +00:00
{
int i ;
for_each_possible_cpu ( i ) {
const struct pcpu_dstats * dstats ;
u64 tbytes , tpackets ;
unsigned int start ;
dstats = per_cpu_ptr ( dev - > dstats , i ) ;
do {
2014-03-13 21:26:42 -07:00
start = u64_stats_fetch_begin_irq ( & dstats - > syncp ) ;
2010-09-27 20:50:33 +00:00
tbytes = dstats - > tx_bytes ;
tpackets = dstats - > tx_packets ;
2014-03-13 21:26:42 -07:00
} while ( u64_stats_fetch_retry_irq ( & dstats - > syncp , start ) ) ;
2010-09-27 20:50:33 +00:00
stats - > tx_bytes + = tbytes ;
stats - > tx_packets + = tpackets ;
}
}
2009-08-31 19:50:51 +00:00
static netdev_tx_t dummy_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2010-09-27 20:50:33 +00:00
struct pcpu_dstats * dstats = this_cpu_ptr ( dev - > dstats ) ;
u64_stats_update_begin ( & dstats - > syncp ) ;
dstats - > tx_packets + + ;
dstats - > tx_bytes + = skb - > len ;
u64_stats_update_end ( & dstats - > syncp ) ;
2009-08-31 19:50:51 +00:00
dev_kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
2010-09-27 20:50:33 +00:00
static int dummy_dev_init ( struct net_device * dev )
{
2017-01-23 12:17:33 +01:00
struct dummy_priv * priv = netdev_priv ( dev ) ;
2014-02-13 11:46:28 -08:00
dev - > dstats = netdev_alloc_pcpu_stats ( struct pcpu_dstats ) ;
2010-09-27 20:50:33 +00:00
if ( ! dev - > dstats )
return - ENOMEM ;
2017-01-23 12:17:33 +01:00
priv - > vfinfo = NULL ;
if ( ! num_vfs )
return 0 ;
dev - > dev . parent = & dummy_parent ;
priv - > vfinfo = kcalloc ( num_vfs , sizeof ( struct vf_data_storage ) ,
GFP_KERNEL ) ;
if ( ! priv - > vfinfo ) {
free_percpu ( dev - > dstats ) ;
return - ENOMEM ;
}
2010-09-27 20:50:33 +00:00
return 0 ;
}
2012-04-15 13:26:01 +00:00
static void dummy_dev_uninit ( struct net_device * dev )
2010-09-27 20:50:33 +00:00
{
free_percpu ( dev - > dstats ) ;
}
2012-12-27 23:49:40 +00:00
static int dummy_change_carrier ( struct net_device * dev , bool new_carrier )
{
if ( new_carrier )
netif_carrier_on ( dev ) ;
else
netif_carrier_off ( dev ) ;
return 0 ;
}
2017-01-23 12:17:33 +01:00
static int dummy_set_vf_mac ( struct net_device * dev , int vf , u8 * mac )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( ! is_valid_ether_addr ( mac ) | | ( vf > = num_vfs ) )
return - EINVAL ;
memcpy ( priv - > vfinfo [ vf ] . vf_mac , mac , ETH_ALEN ) ;
return 0 ;
}
static int dummy_set_vf_vlan ( struct net_device * dev , int vf ,
u16 vlan , u8 qos , __be16 vlan_proto )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( ( vf > = num_vfs ) | | ( vlan > 4095 ) | | ( qos > 7 ) )
return - EINVAL ;
priv - > vfinfo [ vf ] . pf_vlan = vlan ;
priv - > vfinfo [ vf ] . pf_qos = qos ;
priv - > vfinfo [ vf ] . vlan_proto = vlan_proto ;
return 0 ;
}
static int dummy_set_vf_rate ( struct net_device * dev , int vf , int min , int max )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
priv - > vfinfo [ vf ] . min_tx_rate = min ;
priv - > vfinfo [ vf ] . max_tx_rate = max ;
return 0 ;
}
static int dummy_set_vf_spoofchk ( struct net_device * dev , int vf , bool val )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
priv - > vfinfo [ vf ] . spoofchk_enabled = val ;
return 0 ;
}
static int dummy_set_vf_rss_query_en ( struct net_device * dev , int vf , bool val )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
priv - > vfinfo [ vf ] . rss_query_enabled = val ;
return 0 ;
}
static int dummy_set_vf_trust ( struct net_device * dev , int vf , bool val )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
priv - > vfinfo [ vf ] . trusted = val ;
return 0 ;
}
static int dummy_get_vf_config ( struct net_device * dev ,
int vf , struct ifla_vf_info * ivi )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
ivi - > vf = vf ;
memcpy ( & ivi - > mac , priv - > vfinfo [ vf ] . vf_mac , ETH_ALEN ) ;
ivi - > vlan = priv - > vfinfo [ vf ] . pf_vlan ;
ivi - > qos = priv - > vfinfo [ vf ] . pf_qos ;
ivi - > spoofchk = priv - > vfinfo [ vf ] . spoofchk_enabled ;
ivi - > linkstate = priv - > vfinfo [ vf ] . link_state ;
ivi - > min_tx_rate = priv - > vfinfo [ vf ] . min_tx_rate ;
ivi - > max_tx_rate = priv - > vfinfo [ vf ] . max_tx_rate ;
ivi - > rss_query_en = priv - > vfinfo [ vf ] . rss_query_enabled ;
ivi - > trusted = priv - > vfinfo [ vf ] . trusted ;
ivi - > vlan_proto = priv - > vfinfo [ vf ] . vlan_proto ;
return 0 ;
}
static int dummy_set_vf_link_state ( struct net_device * dev , int vf , int state )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
if ( vf > = num_vfs )
return - EINVAL ;
priv - > vfinfo [ vf ] . link_state = state ;
return 0 ;
}
2008-11-20 20:28:00 -08:00
static const struct net_device_ops dummy_netdev_ops = {
2010-09-27 20:50:33 +00:00
. ndo_init = dummy_dev_init ,
2012-04-15 13:26:01 +00:00
. ndo_uninit = dummy_dev_uninit ,
2008-11-20 20:28:00 -08:00
. ndo_start_xmit = dummy_xmit ,
. ndo_validate_addr = eth_validate_addr ,
2011-08-16 06:29:01 +00:00
. ndo_set_rx_mode = set_multicast_list ,
2012-06-29 05:10:08 +00:00
. ndo_set_mac_address = eth_mac_addr ,
2010-09-27 20:50:33 +00:00
. ndo_get_stats64 = dummy_get_stats64 ,
2012-12-27 23:49:40 +00:00
. ndo_change_carrier = dummy_change_carrier ,
2017-01-23 12:17:33 +01:00
. ndo_set_vf_mac = dummy_set_vf_mac ,
. ndo_set_vf_vlan = dummy_set_vf_vlan ,
. ndo_set_vf_rate = dummy_set_vf_rate ,
. ndo_set_vf_spoofchk = dummy_set_vf_spoofchk ,
. ndo_set_vf_trust = dummy_set_vf_trust ,
. ndo_get_vf_config = dummy_get_vf_config ,
. ndo_set_vf_link_state = dummy_set_vf_link_state ,
. ndo_set_vf_rss_query_en = dummy_set_vf_rss_query_en ,
2008-11-20 20:28:00 -08:00
} ;
2014-12-05 22:13:24 -02:00
static void dummy_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
{
strlcpy ( info - > driver , DRV_NAME , sizeof ( info - > driver ) ) ;
strlcpy ( info - > version , DRV_VERSION , sizeof ( info - > version ) ) ;
}
static const struct ethtool_ops dummy_ethtool_ops = {
. get_drvinfo = dummy_get_drvinfo ,
} ;
2017-01-23 12:17:33 +01:00
static void dummy_free_netdev ( struct net_device * dev )
{
struct dummy_priv * priv = netdev_priv ( dev ) ;
kfree ( priv - > vfinfo ) ;
free_netdev ( dev ) ;
}
2007-06-13 12:04:34 -07:00
static void dummy_setup ( struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
2008-11-20 20:28:00 -08:00
ether_setup ( dev ) ;
2005-04-16 15:20:36 -07:00
/* Initialize the device structure. */
2008-11-20 20:28:00 -08:00
dev - > netdev_ops = & dummy_netdev_ops ;
2014-12-05 22:13:24 -02:00
dev - > ethtool_ops = & dummy_ethtool_ops ;
2017-01-23 12:17:33 +01:00
dev - > destructor = dummy_free_netdev ;
2005-04-16 15:20:36 -07:00
/* Fill in device structure with ethernet-generic values. */
dev - > flags | = IFF_NOARP ;
dev - > flags & = ~ IFF_MULTICAST ;
2015-08-18 10:30:30 +02:00
dev - > priv_flags | = IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE ;
2015-10-19 20:17:59 -07:00
dev - > features | = NETIF_F_SG | NETIF_F_FRAGLIST ;
dev - > features | = NETIF_F_ALL_TSO | NETIF_F_UFO ;
2011-11-15 15:29:55 +00:00
dev - > features | = NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX ;
2015-10-19 20:17:59 -07:00
dev - > features | = NETIF_F_GSO_ENCAP_ALL ;
dev - > hw_features | = dev - > features ;
dev - > hw_enc_features | = dev - > features ;
2012-02-15 06:45:40 +00:00
eth_hw_addr_random ( dev ) ;
2016-12-07 17:41:33 +08:00
dev - > min_mtu = 0 ;
dev - > max_mtu = ETH_MAX_MTU ;
2005-04-16 15:20:36 -07:00
}
2010-09-27 20:50:33 +00:00
2007-07-11 19:42:31 -07:00
static int dummy_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
if ( tb [ IFLA_ADDRESS ] ) {
if ( nla_len ( tb [ IFLA_ADDRESS ] ) ! = ETH_ALEN )
return - EINVAL ;
if ( ! is_valid_ether_addr ( nla_data ( tb [ IFLA_ADDRESS ] ) ) )
return - EADDRNOTAVAIL ;
}
return 0 ;
}
2007-06-13 12:04:34 -07:00
static struct rtnl_link_ops dummy_link_ops __read_mostly = {
2014-12-05 22:13:24 -02:00
. kind = DRV_NAME ,
2017-01-23 12:17:33 +01:00
. priv_size = sizeof ( struct dummy_priv ) ,
2007-06-13 12:04:34 -07:00
. setup = dummy_setup ,
2007-07-11 19:42:31 -07:00
. validate = dummy_validate ,
2007-06-13 12:04:34 -07:00
} ;
2005-04-16 15:20:36 -07:00
/* Number of dummy devices to be set up by this module. */
module_param ( numdummies , int , 0 ) ;
MODULE_PARM_DESC ( numdummies , " Number of dummy pseudo devices " ) ;
2017-01-23 12:17:33 +01:00
module_param ( num_vfs , int , 0 ) ;
MODULE_PARM_DESC ( num_vfs , " Number of dummy VFs per dummy device " ) ;
2007-06-13 12:04:20 -07:00
static int __init dummy_init_one ( void )
2005-04-16 15:20:36 -07:00
{
struct net_device * dev_dummy ;
int err ;
2017-01-23 12:17:33 +01:00
dev_dummy = alloc_netdev ( sizeof ( struct dummy_priv ) ,
" dummy%d " , NET_NAME_UNKNOWN , dummy_setup ) ;
2005-04-16 15:20:36 -07:00
if ( ! dev_dummy )
return - ENOMEM ;
2007-06-13 12:04:34 -07:00
dev_dummy - > rtnl_link_ops = & dummy_link_ops ;
err = register_netdevice ( dev_dummy ) ;
if ( err < 0 )
goto err ;
return 0 ;
2007-06-13 12:04:20 -07:00
2007-06-13 12:04:34 -07:00
err :
free_netdev ( dev_dummy ) ;
return err ;
2006-09-13 13:24:59 -04:00
}
2005-04-16 15:20:36 -07:00
static int __init dummy_init_module ( void )
2006-09-13 13:24:59 -04:00
{
2005-04-16 15:20:36 -07:00
int i , err = 0 ;
2007-06-13 12:04:20 -07:00
2017-01-23 12:17:33 +01:00
if ( num_vfs ) {
err = bus_register ( & dummy_bus ) ;
if ( err < 0 ) {
pr_err ( " registering dummy bus failed \n " ) ;
return err ;
}
err = device_register ( & dummy_parent ) ;
if ( err < 0 ) {
pr_err ( " registering dummy parent device failed \n " ) ;
bus_unregister ( & dummy_bus ) ;
return err ;
}
}
2007-06-13 12:04:34 -07:00
rtnl_lock ( ) ;
err = __rtnl_link_register ( & dummy_link_ops ) ;
2013-07-11 19:04:02 +08:00
if ( err < 0 )
goto out ;
2007-06-13 12:04:34 -07:00
2012-06-10 21:11:57 +00:00
for ( i = 0 ; i < numdummies & & ! err ; i + + ) {
2007-06-13 12:04:20 -07:00
err = dummy_init_one ( ) ;
2012-06-10 21:11:57 +00:00
cond_resched ( ) ;
}
2007-07-11 19:42:13 -07:00
if ( err < 0 )
2007-06-13 12:04:34 -07:00
__rtnl_link_unregister ( & dummy_link_ops ) ;
2013-07-11 19:04:02 +08:00
out :
2007-06-13 12:04:34 -07:00
rtnl_unlock ( ) ;
2017-01-23 12:17:33 +01:00
if ( err & & num_vfs ) {
device_unregister ( & dummy_parent ) ;
bus_unregister ( & dummy_bus ) ;
}
2005-04-16 15:20:36 -07:00
return err ;
2006-09-13 13:24:59 -04:00
}
2005-04-16 15:20:36 -07:00
static void __exit dummy_cleanup_module ( void )
{
2007-07-11 19:42:13 -07:00
rtnl_link_unregister ( & dummy_link_ops ) ;
2017-01-23 12:17:33 +01:00
if ( num_vfs ) {
device_unregister ( & dummy_parent ) ;
bus_unregister ( & dummy_bus ) ;
}
2005-04-16 15:20:36 -07:00
}
module_init ( dummy_init_module ) ;
module_exit ( dummy_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-12-05 22:13:24 -02:00
MODULE_ALIAS_RTNL_LINK ( DRV_NAME ) ;
2014-12-09 22:41:48 -02:00
MODULE_VERSION ( DRV_VERSION ) ;