dummy.c 4.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/* 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
14

L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
	[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, 27th May 1994
	[I tweaked this explanation a little but that's all]
			Alan Cox, 30th 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>
37
#include <linux/rtnetlink.h>
38
#include <net/rtnetlink.h>
39
#include <linux/u64_stats_sync.h>
40

L
Linus Torvalds 已提交
41 42 43 44 45 46 47
static int numdummies = 1;

/* fake multicast ability */
static void set_multicast_list(struct net_device *dev)
{
}

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
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 {
66
			start = u64_stats_fetch_begin_bh(&dstats->syncp);
67 68
			tbytes = dstats->tx_bytes;
			tpackets = dstats->tx_packets;
69
		} while (u64_stats_fetch_retry_bh(&dstats->syncp, start));
70 71 72 73 74
		stats->tx_bytes += tbytes;
		stats->tx_packets += tpackets;
	}
	return stats;
}
75 76 77

static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
{
78 79 80 81 82 83
	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);
84 85 86 87 88

	dev_kfree_skb(skb);
	return NETDEV_TX_OK;
}

89 90 91 92 93 94 95 96 97
static int dummy_dev_init(struct net_device *dev)
{
	dev->dstats = alloc_percpu(struct pcpu_dstats);
	if (!dev->dstats)
		return -ENOMEM;

	return 0;
}

98
static void dummy_dev_uninit(struct net_device *dev)
99 100 101 102
{
	free_percpu(dev->dstats);
}

103
static const struct net_device_ops dummy_netdev_ops = {
104
	.ndo_init		= dummy_dev_init,
105
	.ndo_uninit		= dummy_dev_uninit,
106 107
	.ndo_start_xmit		= dummy_xmit,
	.ndo_validate_addr	= eth_validate_addr,
108
	.ndo_set_rx_mode	= set_multicast_list,
109
	.ndo_set_mac_address	= eth_mac_addr,
110
	.ndo_get_stats64	= dummy_get_stats64,
111 112
};

113
static void dummy_setup(struct net_device *dev)
L
Linus Torvalds 已提交
114
{
115 116
	ether_setup(dev);

L
Linus Torvalds 已提交
117
	/* Initialize the device structure. */
118
	dev->netdev_ops = &dummy_netdev_ops;
119
	dev->destructor = free_netdev;
L
Linus Torvalds 已提交
120 121 122 123 124

	/* Fill in device structure with ethernet-generic values. */
	dev->tx_queue_len = 0;
	dev->flags |= IFF_NOARP;
	dev->flags &= ~IFF_MULTICAST;
125
	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
126
	dev->features	|= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
127
	dev->features	|= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
128
	eth_hw_addr_random(dev);
L
Linus Torvalds 已提交
129
}
130

131 132 133 134 135 136 137 138 139 140 141
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;
}

142 143 144
static struct rtnl_link_ops dummy_link_ops __read_mostly = {
	.kind		= "dummy",
	.setup		= dummy_setup,
145
	.validate	= dummy_validate,
146 147
};

L
Linus Torvalds 已提交
148 149 150 151
/* 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");

152
static int __init dummy_init_one(void)
L
Linus Torvalds 已提交
153 154 155 156
{
	struct net_device *dev_dummy;
	int err;

157
	dev_dummy = alloc_netdev(0, "dummy%d", dummy_setup);
L
Linus Torvalds 已提交
158 159 160
	if (!dev_dummy)
		return -ENOMEM;

161 162 163 164 165
	dev_dummy->rtnl_link_ops = &dummy_link_ops;
	err = register_netdevice(dev_dummy);
	if (err < 0)
		goto err;
	return 0;
166

167 168 169
err:
	free_netdev(dev_dummy);
	return err;
170
}
L
Linus Torvalds 已提交
171 172

static int __init dummy_init_module(void)
173
{
L
Linus Torvalds 已提交
174
	int i, err = 0;
175

176 177 178
	rtnl_lock();
	err = __rtnl_link_register(&dummy_link_ops);

179
	for (i = 0; i < numdummies && !err; i++) {
180
		err = dummy_init_one();
181 182
		cond_resched();
	}
183
	if (err < 0)
184 185 186
		__rtnl_link_unregister(&dummy_link_ops);
	rtnl_unlock();

L
Linus Torvalds 已提交
187
	return err;
188
}
L
Linus Torvalds 已提交
189 190 191

static void __exit dummy_cleanup_module(void)
{
192
	rtnl_link_unregister(&dummy_link_ops);
L
Linus Torvalds 已提交
193 194 195 196 197
}

module_init(dummy_init_module);
module_exit(dummy_cleanup_module);
MODULE_LICENSE("GPL");
198
MODULE_ALIAS_RTNL_LINK("dummy");
新手
引导
客服 返回
顶部