dummy.c 4.9 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
static int numdummies = 1;

static int dummy_set_address(struct net_device *dev, void *p)
{
	struct sockaddr *sa = p;

47
	if (!is_valid_ether_addr(sa->sa_data))
L
Linus Torvalds 已提交
48
		return -EADDRNOTAVAIL;
49

L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58
	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
	return 0;
}

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

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
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;
}
86 87 88

static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
{
89 90 91 92 93 94
	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);
95 96 97 98 99

	dev_kfree_skb(skb);
	return NETDEV_TX_OK;
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
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);
}

115
static const struct net_device_ops dummy_netdev_ops = {
116
	.ndo_init		= dummy_dev_init,
117 118
	.ndo_start_xmit		= dummy_xmit,
	.ndo_validate_addr	= eth_validate_addr,
119
	.ndo_set_rx_mode	= set_multicast_list,
120
	.ndo_set_mac_address	= dummy_set_address,
121
	.ndo_get_stats64	= dummy_get_stats64,
122 123
};

124
static void dummy_setup(struct net_device *dev)
L
Linus Torvalds 已提交
125
{
126 127
	ether_setup(dev);

L
Linus Torvalds 已提交
128
	/* Initialize the device structure. */
129
	dev->netdev_ops = &dummy_netdev_ops;
130
	dev->destructor = dummy_dev_free;
L
Linus Torvalds 已提交
131 132 133 134 135

	/* Fill in device structure with ethernet-generic values. */
	dev->tx_queue_len = 0;
	dev->flags |= IFF_NOARP;
	dev->flags &= ~IFF_MULTICAST;
136
	dev->features	|= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
137
	dev->features	|= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
L
Linus Torvalds 已提交
138 139
	random_ether_addr(dev->dev_addr);
}
140

141 142 143 144 145 146 147 148 149 150 151
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;
}

152 153 154
static struct rtnl_link_ops dummy_link_ops __read_mostly = {
	.kind		= "dummy",
	.setup		= dummy_setup,
155
	.validate	= dummy_validate,
156 157
};

L
Linus Torvalds 已提交
158 159 160 161
/* 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");

162
static int __init dummy_init_one(void)
L
Linus Torvalds 已提交
163 164 165 166
{
	struct net_device *dev_dummy;
	int err;

167
	dev_dummy = alloc_netdev(0, "dummy%d", dummy_setup);
L
Linus Torvalds 已提交
168 169 170
	if (!dev_dummy)
		return -ENOMEM;

171 172 173 174 175
	dev_dummy->rtnl_link_ops = &dummy_link_ops;
	err = register_netdevice(dev_dummy);
	if (err < 0)
		goto err;
	return 0;
176

177 178 179
err:
	free_netdev(dev_dummy);
	return err;
180
}
L
Linus Torvalds 已提交
181 182

static int __init dummy_init_module(void)
183
{
L
Linus Torvalds 已提交
184
	int i, err = 0;
185

186 187 188
	rtnl_lock();
	err = __rtnl_link_register(&dummy_link_ops);

L
Linus Torvalds 已提交
189
	for (i = 0; i < numdummies && !err; i++)
190
		err = dummy_init_one();
191
	if (err < 0)
192 193 194
		__rtnl_link_unregister(&dummy_link_ops);
	rtnl_unlock();

L
Linus Torvalds 已提交
195
	return err;
196
}
L
Linus Torvalds 已提交
197 198 199

static void __exit dummy_cleanup_module(void)
{
200
	rtnl_link_unregister(&dummy_link_ops);
L
Linus Torvalds 已提交
201 202 203 204 205
}

module_init(dummy_init_module);
module_exit(dummy_cleanup_module);
MODULE_LICENSE("GPL");
206
MODULE_ALIAS_RTNL_LINK("dummy");
反馈
建议
客服 返回
顶部