dummy.c 4.92 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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

Linus Torvalds's avatar
Linus Torvalds committed
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>
Patrick McHardy's avatar
Patrick McHardy committed
38
#include <net/rtnetlink.h>
39
#include <linux/u64_stats_sync.h>
40

Linus Torvalds's avatar
Linus Torvalds committed
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;
}

Hiroaki SHIMODA's avatar
Hiroaki SHIMODA committed
98
static void dummy_dev_uninit(struct net_device *dev)
99
100
101
102
{
	free_percpu(dev->dstats);
}

Jiri Pirko's avatar
Jiri Pirko committed
103
104
105
106
107
108
109
110
111
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;
}

112
static const struct net_device_ops dummy_netdev_ops = {
113
	.ndo_init		= dummy_dev_init,
Hiroaki SHIMODA's avatar
Hiroaki SHIMODA committed
114
	.ndo_uninit		= dummy_dev_uninit,
115
116
	.ndo_start_xmit		= dummy_xmit,
	.ndo_validate_addr	= eth_validate_addr,
117
	.ndo_set_rx_mode	= set_multicast_list,
118
	.ndo_set_mac_address	= eth_mac_addr,
119
	.ndo_get_stats64	= dummy_get_stats64,
Jiri Pirko's avatar
Jiri Pirko committed
120
	.ndo_change_carrier	= dummy_change_carrier,
121
122
};

Patrick McHardy's avatar
Patrick McHardy committed
123
static void dummy_setup(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
124
{
125
126
	ether_setup(dev);

Linus Torvalds's avatar
Linus Torvalds committed
127
	/* Initialize the device structure. */
128
	dev->netdev_ops = &dummy_netdev_ops;
Hiroaki SHIMODA's avatar
Hiroaki SHIMODA committed
129
	dev->destructor = free_netdev;
Linus Torvalds's avatar
Linus Torvalds committed
130
131
132
133
134

	/* Fill in device structure with ethernet-generic values. */
	dev->tx_queue_len = 0;
	dev->flags |= IFF_NOARP;
	dev->flags &= ~IFF_MULTICAST;
135
	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
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;
138
	eth_hw_addr_random(dev);
Linus Torvalds's avatar
Linus Torvalds committed
139
}
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;
}

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

Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
163
164
165
166
{
	struct net_device *dev_dummy;
	int err;

167
	dev_dummy = alloc_netdev(0, "dummy%d", dummy_setup);
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
	if (!dev_dummy)
		return -ENOMEM;

Patrick McHardy's avatar
Patrick McHardy committed
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

Patrick McHardy's avatar
Patrick McHardy committed
177
178
179
err:
	free_netdev(dev_dummy);
	return err;
180
}
Linus Torvalds's avatar
Linus Torvalds committed
181
182

static int __init dummy_init_module(void)
183
{
Linus Torvalds's avatar
Linus Torvalds committed
184
	int i, err = 0;
185

Patrick McHardy's avatar
Patrick McHardy committed
186
187
188
	rtnl_lock();
	err = __rtnl_link_register(&dummy_link_ops);

189
	for (i = 0; i < numdummies && !err; i++) {
190
		err = dummy_init_one();
191
192
		cond_resched();
	}
193
	if (err < 0)
Patrick McHardy's avatar
Patrick McHardy committed
194
195
196
		__rtnl_link_unregister(&dummy_link_ops);
	rtnl_unlock();

Linus Torvalds's avatar
Linus Torvalds committed
197
	return err;
198
}
Linus Torvalds's avatar
Linus Torvalds committed
199
200
201

static void __exit dummy_cleanup_module(void)
{
202
	rtnl_link_unregister(&dummy_link_ops);
Linus Torvalds's avatar
Linus Torvalds committed
203
204
205
206
207
}

module_init(dummy_init_module);
module_exit(dummy_cleanup_module);
MODULE_LICENSE("GPL");
Patrick McHardy's avatar
Patrick McHardy committed
208
MODULE_ALIAS_RTNL_LINK("dummy");