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 ;
static int dummy_set_address ( struct net_device * dev , void * p )
{
struct sockaddr * sa = p ;
2006-09-13 21:24:59 +04:00
if ( ! is_valid_ether_addr ( sa - > sa_data ) )
2005-04-17 02:20:36 +04:00
return - EADDRNOTAVAIL ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
memcpy ( dev - > dev_addr , sa - > sa_data , ETH_ALEN ) ;
return 0 ;
}
/* 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 {
start = u64_stats_fetch_begin ( & dstats - > syncp ) ;
tbytes = dstats - > tx_bytes ;
tpackets = dstats - > tx_packets ;
} while ( u64_stats_fetch_retry ( & dstats - > syncp , start ) ) ;
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 )
{
dev - > dstats = alloc_percpu ( struct pcpu_dstats ) ;
if ( ! dev - > dstats )
return - ENOMEM ;
return 0 ;
}
static void dummy_dev_free ( struct net_device * dev )
{
free_percpu ( dev - > dstats ) ;
free_netdev ( dev ) ;
}
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 ,
2008-11-21 07:28:00 +03:00
. ndo_start_xmit = dummy_xmit ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_multicast_list = set_multicast_list ,
. ndo_set_mac_address = dummy_set_address ,
2010-09-28 00:50:33 +04:00
. ndo_get_stats64 = dummy_get_stats64 ,
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 ;
2010-09-28 00:50:33 +04:00
dev - > destructor = dummy_dev_free ;
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 ;
2010-09-28 00:50:33 +04:00
dev - > features | = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO ;
dev - > features | = NETIF_F_NO_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX ;
2005-04-17 02:20:36 +04:00
random_ether_addr ( dev - > dev_addr ) ;
}
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
err = dev_alloc_name ( dev_dummy , dev_dummy - > name ) ;
if ( err < 0 )
goto err ;
2005-04-17 02:20:36 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < numdummies & & ! err ; i + + )
2007-06-13 23:04:20 +04:00
err = dummy_init_one ( ) ;
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 ) ;
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 " ) ;