11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* dummy.c: a dummy net driver
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The purpose of this driver is to provide a device to point a
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	route through, but not to actually transmit packets.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Why?  If you have a machine whose only connection is an occasional
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PPP/SLIP/PLIP link, you can only connect to your own hostname
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	when the link is up.  Otherwise you have to use localhost.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This isn't very consistent.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	One solution is to set up a dummy link using PPP/SLIP/PLIP,
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	but this seems (to me) too much overhead for too little gain.
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver provides a small alternative. Thus you can do
146aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[when not running slip]
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ifconfig dummy slip.addr.ess.here up
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[to go to slip]
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ifconfig dummy down
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dip whatever
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This was written by looking at Donald Becker's skeleton driver
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	and the loopback driver.  I then threw away anything that didn't
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	apply!	Thanks to Alan Cox for the key clue on what to do with
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misguided packets.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			Nick Holloway, 27th May 1994
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[I tweaked this explanation a little but that's all]
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			Alan Cox, 30th May 1994
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
37206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardy#include <linux/rtnetlink.h>
385d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy#include <net/rtnetlink.h>
396d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet#include <linux/u64_stats_sync.h>
40206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardy
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int numdummies = 1;
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_set_address(struct net_device *dev, void *p)
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sockaddr *sa = p;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
476aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	if (!is_valid_ether_addr(sa->sa_data))
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EADDRNOTAVAIL;
496aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
507ce5d222190cb3ce3ae88bafde7c4fa52a5103e0Danny Kukawka	dev->addr_assign_type &= ~NET_ADDR_RANDOM;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fake multicast ability */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void set_multicast_list(struct net_device *dev)
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
606d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazetstruct pcpu_dstats {
616d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	u64			tx_packets;
626d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	u64			tx_bytes;
636d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	struct u64_stats_sync	syncp;
646d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet};
656d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
666d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazetstatic struct rtnl_link_stats64 *dummy_get_stats64(struct net_device *dev,
676d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet						   struct rtnl_link_stats64 *stats)
686d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet{
696d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	int i;
706d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
716d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	for_each_possible_cpu(i) {
726d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		const struct pcpu_dstats *dstats;
736d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		u64 tbytes, tpackets;
746d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		unsigned int start;
756d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
766d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		dstats = per_cpu_ptr(dev->dstats, i);
776d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		do {
786d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet			start = u64_stats_fetch_begin(&dstats->syncp);
796d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet			tbytes = dstats->tx_bytes;
806d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet			tpackets = dstats->tx_packets;
816d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		} while (u64_stats_fetch_retry(&dstats->syncp, start));
826d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		stats->tx_bytes += tbytes;
836d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		stats->tx_packets += tpackets;
846d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	}
856d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	return stats;
866d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet}
87424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger
88424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemmingerstatic netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
89424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger{
906d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
916d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
926d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	u64_stats_update_begin(&dstats->syncp);
936d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	dstats->tx_packets++;
946d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	dstats->tx_bytes += skb->len;
956d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	u64_stats_update_end(&dstats->syncp);
96424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger
97424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger	dev_kfree_skb(skb);
98424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger	return NETDEV_TX_OK;
99424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger}
100424efe9caf6047ffbcd6b383ff4d2347254aabf1Stephen Hemminger
1016d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazetstatic int dummy_dev_init(struct net_device *dev)
1026d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet{
1036d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	dev->dstats = alloc_percpu(struct pcpu_dstats);
1046d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	if (!dev->dstats)
1056d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet		return -ENOMEM;
1066d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
1076d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	return 0;
1086d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet}
1096d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
110890fdf2a0cb88202d1427589db2cf29c1bdd3c1dHiroaki SHIMODAstatic void dummy_dev_uninit(struct net_device *dev)
1116d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet{
1126d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	free_percpu(dev->dstats);
1136d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet}
1146d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
115aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemmingerstatic const struct net_device_ops dummy_netdev_ops = {
1166d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	.ndo_init		= dummy_dev_init,
117890fdf2a0cb88202d1427589db2cf29c1bdd3c1dHiroaki SHIMODA	.ndo_uninit		= dummy_dev_uninit,
118aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger	.ndo_start_xmit		= dummy_xmit,
119aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
120afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= set_multicast_list,
121aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger	.ndo_set_mac_address	= dummy_set_address,
1226d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	.ndo_get_stats64	= dummy_get_stats64,
123aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger};
124aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger
1255d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardystatic void dummy_setup(struct net_device *dev)
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
127aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger	ether_setup(dev);
128aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize the device structure. */
130aa18e9e88c863a58f6ea63eeee0e740be071fdcfStephen Hemminger	dev->netdev_ops = &dummy_netdev_ops;
131890fdf2a0cb88202d1427589db2cf29c1bdd3c1dHiroaki SHIMODA	dev->destructor = free_netdev;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Fill in device structure with ethernet-generic values. */
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_queue_len = 0;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->flags |= IFF_NOARP;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->flags &= ~IFF_MULTICAST;
1376d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet	dev->features	|= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
13834324dc2bf27c1773045fea63cb11f7e2a6ad2b9Michał Mirosław	dev->features	|= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
1397ce5d222190cb3ce3ae88bafde7c4fa52a5103e0Danny Kukawka	eth_hw_addr_random(dev);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1416d81f41c58c69ddde497e9e640ba5805aa26e78cEric Dumazet
1420e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardystatic int dummy_validate(struct nlattr *tb[], struct nlattr *data[])
1430e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy{
1440e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy	if (tb[IFLA_ADDRESS]) {
1450e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
1460e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy			return -EINVAL;
1470e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
1480e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy			return -EADDRNOTAVAIL;
1490e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy	}
1500e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy	return 0;
1510e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy}
1520e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy
1535d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardystatic struct rtnl_link_ops dummy_link_ops __read_mostly = {
1545d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	.kind		= "dummy",
1555d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	.setup		= dummy_setup,
1560e06877c6fdbc67b1132be895f995acd1ff30135Patrick McHardy	.validate	= dummy_validate,
1575d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy};
1585d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Number of dummy devices to be set up by this module. */
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(numdummies, int, 0);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices");
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
163206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardystatic int __init dummy_init_one(void)
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev_dummy;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1682d85cba2b272a5201a60966a65a4f8c0bcc0bb71Patrick McHardy	dev_dummy = alloc_netdev(0, "dummy%d", dummy_setup);
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev_dummy)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1725d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	dev_dummy->rtnl_link_ops = &dummy_link_ops;
1735d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	err = register_netdevice(dev_dummy);
1745d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	if (err < 0)
1755d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy		goto err;
1765d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	return 0;
177206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardy
1785d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardyerr:
1795d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	free_netdev(dev_dummy);
1805d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	return err;
1816aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init dummy_init_module(void)
1846aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik{
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, err = 0;
186206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardy
1875d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	rtnl_lock();
1885d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	err = __rtnl_link_register(&dummy_link_ops);
1895d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy
19009838df9d2ff31252a69a8393ea96a9734eba7e8Eric Dumazet	for (i = 0; i < numdummies && !err; i++) {
191206c9fb26f5df2ea6d440fb64159faf4d8665398Patrick McHardy		err = dummy_init_one();
19209838df9d2ff31252a69a8393ea96a9734eba7e8Eric Dumazet		cond_resched();
19309838df9d2ff31252a69a8393ea96a9734eba7e8Eric Dumazet	}
1942d85cba2b272a5201a60966a65a4f8c0bcc0bb71Patrick McHardy	if (err < 0)
1955d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy		__rtnl_link_unregister(&dummy_link_ops);
1965d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy	rtnl_unlock();
1975d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardy
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
1996aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit dummy_cleanup_module(void)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2032d85cba2b272a5201a60966a65a4f8c0bcc0bb71Patrick McHardy	rtnl_link_unregister(&dummy_link_ops);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(dummy_init_module);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(dummy_cleanup_module);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
2095d5cb173d85ebf6dfb16f456a8148ecb4b1cecbcPatrick McHardyMODULE_ALIAS_RTNL_LINK("dummy");
210