2005-04-17 02:20:36 +04: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 21:24:59 +04:00
2005-04-17 02:20:36 +04: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 23:04:20 +04:00
# include <linux/rtnetlink.h>
2007-06-13 23:04:34 +04:00
# include <net/rtnetlink.h>
2010-09-28 00:50:33 +04:00
# include <linux/u64_stats_sync.h>
2007-06-13 23:04:20 +04:00
2005-04-17 02:20:36 +04:00
static int numdummies = 1 ;
/* fake multicast ability */
static void set_multicast_list ( struct net_device * dev )
{
}
2010-09-28 00:50:33 +04:00
struct pcpu_dstats {
u64 tx_packets ;
u64 tx_bytes ;
struct u64_stats_sync syncp ;
} ;
static struct rtnl_link_stats64 * dummy_get_stats64 ( struct net_device * dev ,
struct rtnl_link_stats64 * stats )
{
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 {
2012-07-21 10:30:50 +04:00
start = u64_stats_fetch_begin_bh ( & dstats - > syncp ) ;
2010-09-28 00:50:33 +04:00
tbytes = dstats - > tx_bytes ;
tpackets = dstats - > tx_packets ;
2012-07-21 10:30:50 +04:00
} while ( u64_stats_fetch_retry_bh ( & dstats - > syncp , start ) ) ;
2010-09-28 00:50:33 +04:00
stats - > tx_bytes + = tbytes ;
stats - > tx_packets + = tpackets ;
}
return stats ;
}
2009-08-31 23:50:51 +04:00
static netdev_tx_t dummy_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2010-09-28 00:50:33 +04: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 23:50:51 +04:00
dev_kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
2010-09-28 00:50:33 +04:00
static int dummy_dev_init ( struct net_device * dev )
{
2013-10-08 02:51:58 +04:00
int i ;
2010-09-28 00:50:33 +04:00
dev - > dstats = alloc_percpu ( struct pcpu_dstats ) ;
if ( ! dev - > dstats )
return - ENOMEM ;
2013-10-08 02:51:58 +04:00
for_each_possible_cpu ( i ) {
struct pcpu_dstats * dstats ;
dstats = per_cpu_ptr ( dev - > dstats , i ) ;
u64_stats_init ( & dstats - > syncp ) ;
}
2010-09-28 00:50:33 +04:00
return 0 ;
}
2012-04-15 17:26:01 +04:00
static void dummy_dev_uninit ( struct net_device * dev )
2010-09-28 00:50:33 +04:00
{
free_percpu ( dev - > dstats ) ;
}
2012-12-28 03:49:40 +04: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 ;
}
2008-11-21 07:28:00 +03:00
static const struct net_device_ops dummy_netdev_ops = {
2010-09-28 00:50:33 +04:00
. ndo_init = dummy_dev_init ,
2012-04-15 17:26:01 +04:00
. ndo_uninit = dummy_dev_uninit ,
2008-11-21 07:28:00 +03:00
. ndo_start_xmit = dummy_xmit ,
. ndo_validate_addr = eth_validate_addr ,
2011-08-16 10:29:01 +04:00
. ndo_set_rx_mode = set_multicast_list ,
2012-06-29 09:10:08 +04:00
. ndo_set_mac_address = eth_mac_addr ,
2010-09-28 00:50:33 +04:00
. ndo_get_stats64 = dummy_get_stats64 ,
2012-12-28 03:49:40 +04:00
. ndo_change_carrier = dummy_change_carrier ,
2008-11-21 07:28:00 +03:00
} ;
2007-06-13 23:04:34 +04:00
static void dummy_setup ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-11-21 07:28:00 +03:00
ether_setup ( dev ) ;
2005-04-17 02:20:36 +04:00
/* Initialize the device structure. */
2008-11-21 07:28:00 +03:00
dev - > netdev_ops = & dummy_netdev_ops ;
2012-04-15 17:26:01 +04:00
dev - > destructor = free_netdev ;
2005-04-17 02:20:36 +04:00
/* Fill in device structure with ethernet-generic values. */
dev - > tx_queue_len = 0 ;
dev - > flags | = IFF_NOARP ;
dev - > flags & = ~ IFF_MULTICAST ;
2012-06-29 09:10:08 +04:00
dev - > priv_flags | = IFF_LIVE_ADDR_CHANGE ;
2010-09-28 00:50:33 +04:00
dev - > features | = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO ;
2011-11-15 19:29:55 +04:00
dev - > features | = NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX ;
2012-02-15 10:45:40 +04:00
eth_hw_addr_random ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2010-09-28 00:50:33 +04:00
2007-07-12 06:42:31 +04: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 23:04:34 +04:00
static struct rtnl_link_ops dummy_link_ops __read_mostly = {
. kind = " dummy " ,
. setup = dummy_setup ,
2007-07-12 06:42:31 +04:00
. validate = dummy_validate ,
2007-06-13 23:04:34 +04:00
} ;
2005-04-17 02:20:36 +04: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 " ) ;
2007-06-13 23:04:20 +04:00
static int __init dummy_init_one ( void )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev_dummy ;
int err ;
2007-07-12 06:42:13 +04:00
dev_dummy = alloc_netdev ( 0 , " dummy%d " , dummy_setup ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev_dummy )
return - ENOMEM ;
2007-06-13 23:04:34 +04:00
dev_dummy - > rtnl_link_ops = & dummy_link_ops ;
err = register_netdevice ( dev_dummy ) ;
if ( err < 0 )
goto err ;
return 0 ;
2007-06-13 23:04:20 +04:00
2007-06-13 23:04:34 +04:00
err :
free_netdev ( dev_dummy ) ;
return err ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
static int __init dummy_init_module ( void )
2006-09-13 21:24:59 +04:00
{
2005-04-17 02:20:36 +04:00
int i , err = 0 ;
2007-06-13 23:04:20 +04:00
2007-06-13 23:04:34 +04:00
rtnl_lock ( ) ;
err = __rtnl_link_register ( & dummy_link_ops ) ;
2013-07-11 15:04:02 +04:00
if ( err < 0 )
goto out ;
2007-06-13 23:04:34 +04:00
2012-06-11 01:11:57 +04:00
for ( i = 0 ; i < numdummies & & ! err ; i + + ) {
2007-06-13 23:04:20 +04:00
err = dummy_init_one ( ) ;
2012-06-11 01:11:57 +04:00
cond_resched ( ) ;
}
2007-07-12 06:42:13 +04:00
if ( err < 0 )
2007-06-13 23:04:34 +04:00
__rtnl_link_unregister ( & dummy_link_ops ) ;
2013-07-11 15:04:02 +04:00
out :
2007-06-13 23:04:34 +04:00
rtnl_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return err ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
static void __exit dummy_cleanup_module ( void )
{
2007-07-12 06:42:13 +04:00
rtnl_link_unregister ( & dummy_link_ops ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( dummy_init_module ) ;
module_exit ( dummy_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-06-13 23:04:34 +04:00
MODULE_ALIAS_RTNL_LINK ( " dummy " ) ;