blob: 54e4d8b07f0e054b2fb83f4ea05063295a544f5b [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/* dummy.c: a dummy net driver
3
4 The purpose of this driver is to provide a device to point a
5 route through, but not to actually transmit packets.
6
7 Why? If you have a machine whose only connection is an occasional
8 PPP/SLIP/PLIP link, you can only connect to your own hostname
9 when the link is up. Otherwise you have to use localhost.
10 This isn't very consistent.
11
12 One solution is to set up a dummy link using PPP/SLIP/PLIP,
13 but this seems (to me) too much overhead for too little gain.
14 This driver provides a small alternative. Thus you can do
Jeff Garzik6aa20a22006-09-13 13:24:59 -040015
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 [when not running slip]
17 ifconfig dummy slip.addr.ess.here up
18 [to go to slip]
19 ifconfig dummy down
20 dip whatever
21
22 This was written by looking at Donald Becker's skeleton driver
23 and the loopback driver. I then threw away anything that didn't
24 apply! Thanks to Alan Cox for the key clue on what to do with
25 misguided packets.
26
27 Nick Holloway, 27th May 1994
28 [I tweaked this explanation a little but that's all]
29 Alan Cox, 30th May 1994
30*/
31
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/module.h>
33#include <linux/kernel.h>
34#include <linux/netdevice.h>
35#include <linux/etherdevice.h>
Julian Wiedmannabe9fd52019-04-12 13:06:13 +020036#include <linux/ethtool.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
38#include <linux/moduleparam.h>
Patrick McHardy206c9fb2007-06-13 12:04:20 -070039#include <linux/rtnetlink.h>
Ezequiel Lara Gomez6df014c2017-03-11 20:06:54 +000040#include <linux/net_tstamp.h>
Patrick McHardy5d5cb172007-06-13 12:04:34 -070041#include <net/rtnetlink.h>
Eric Dumazet6d81f412010-09-27 20:50:33 +000042#include <linux/u64_stats_sync.h>
Patrick McHardy206c9fb2007-06-13 12:04:20 -070043
Flavio Leitnerc19be732014-12-05 22:13:24 -020044#define DRV_NAME "dummy"
45#define DRV_VERSION "1.0"
46
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static int numdummies = 1;
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/* fake multicast ability */
50static void set_multicast_list(struct net_device *dev)
51{
52}
53
Eric Dumazet6d81f412010-09-27 20:50:33 +000054struct pcpu_dstats {
55 u64 tx_packets;
56 u64 tx_bytes;
57 struct u64_stats_sync syncp;
58};
59
stephen hemmingerbc1f4472017-01-06 19:12:52 -080060static void dummy_get_stats64(struct net_device *dev,
61 struct rtnl_link_stats64 *stats)
Eric Dumazet6d81f412010-09-27 20:50:33 +000062{
63 int i;
64
65 for_each_possible_cpu(i) {
66 const struct pcpu_dstats *dstats;
67 u64 tbytes, tpackets;
68 unsigned int start;
69
70 dstats = per_cpu_ptr(dev->dstats, i);
71 do {
Eric W. Biederman57a77442014-03-13 21:26:42 -070072 start = u64_stats_fetch_begin_irq(&dstats->syncp);
Eric Dumazet6d81f412010-09-27 20:50:33 +000073 tbytes = dstats->tx_bytes;
74 tpackets = dstats->tx_packets;
Eric W. Biederman57a77442014-03-13 21:26:42 -070075 } while (u64_stats_fetch_retry_irq(&dstats->syncp, start));
Eric Dumazet6d81f412010-09-27 20:50:33 +000076 stats->tx_bytes += tbytes;
77 stats->tx_packets += tpackets;
78 }
Eric Dumazet6d81f412010-09-27 20:50:33 +000079}
Stephen Hemminger424efe92009-08-31 19:50:51 +000080
81static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
82{
Eric Dumazet6d81f412010-09-27 20:50:33 +000083 struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
84
85 u64_stats_update_begin(&dstats->syncp);
86 dstats->tx_packets++;
87 dstats->tx_bytes += skb->len;
88 u64_stats_update_end(&dstats->syncp);
Stephen Hemminger424efe92009-08-31 19:50:51 +000089
Ezequiel Lara Gomez6df014c2017-03-11 20:06:54 +000090 skb_tx_timestamp(skb);
Stephen Hemminger424efe92009-08-31 19:50:51 +000091 dev_kfree_skb(skb);
92 return NETDEV_TX_OK;
93}
94
Eric Dumazet6d81f412010-09-27 20:50:33 +000095static int dummy_dev_init(struct net_device *dev)
96{
WANG Cong1c213bd2014-02-13 11:46:28 -080097 dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
Eric Dumazet6d81f412010-09-27 20:50:33 +000098 if (!dev->dstats)
99 return -ENOMEM;
100
101 return 0;
102}
103
Hiroaki SHIMODA890fdf22012-04-15 13:26:01 +0000104static void dummy_dev_uninit(struct net_device *dev)
Eric Dumazet6d81f412010-09-27 20:50:33 +0000105{
106 free_percpu(dev->dstats);
Eric Dumazet6d81f412010-09-27 20:50:33 +0000107}
108
Jiri Pirko210ab662012-12-27 23:49:40 +0000109static int dummy_change_carrier(struct net_device *dev, bool new_carrier)
110{
111 if (new_carrier)
112 netif_carrier_on(dev);
113 else
114 netif_carrier_off(dev);
115 return 0;
116}
117
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800118static const struct net_device_ops dummy_netdev_ops = {
Eric Dumazet6d81f412010-09-27 20:50:33 +0000119 .ndo_init = dummy_dev_init,
Hiroaki SHIMODA890fdf22012-04-15 13:26:01 +0000120 .ndo_uninit = dummy_dev_uninit,
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800121 .ndo_start_xmit = dummy_xmit,
122 .ndo_validate_addr = eth_validate_addr,
Jiri Pirkoafc4b132011-08-16 06:29:01 +0000123 .ndo_set_rx_mode = set_multicast_list,
Jiri Pirko0d1632b2012-06-29 05:10:08 +0000124 .ndo_set_mac_address = eth_mac_addr,
Eric Dumazet6d81f412010-09-27 20:50:33 +0000125 .ndo_get_stats64 = dummy_get_stats64,
Jiri Pirko210ab662012-12-27 23:49:40 +0000126 .ndo_change_carrier = dummy_change_carrier,
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800127};
128
Flavio Leitnerc19be732014-12-05 22:13:24 -0200129static void dummy_get_drvinfo(struct net_device *dev,
130 struct ethtool_drvinfo *info)
131{
132 strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
133 strlcpy(info->version, DRV_VERSION, sizeof(info->version));
134}
135
136static const struct ethtool_ops dummy_ethtool_ops = {
137 .get_drvinfo = dummy_get_drvinfo,
Julian Wiedmannabe9fd52019-04-12 13:06:13 +0200138 .get_ts_info = ethtool_op_get_ts_info,
Flavio Leitnerc19be732014-12-05 22:13:24 -0200139};
140
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700141static void dummy_setup(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800143 ether_setup(dev);
144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 /* Initialize the device structure. */
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800146 dev->netdev_ops = &dummy_netdev_ops;
Flavio Leitnerc19be732014-12-05 22:13:24 -0200147 dev->ethtool_ops = &dummy_ethtool_ops;
David S. Millercf124db2017-05-08 12:52:56 -0400148 dev->needs_free_netdev = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 /* Fill in device structure with ethernet-generic values. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 dev->flags |= IFF_NOARP;
152 dev->flags &= ~IFF_MULTICAST;
Phil Sutterff42c022015-08-18 10:30:30 +0200153 dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
Eric Dumazet8f3af272015-10-19 20:17:59 -0700154 dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST;
David S. Miller20824992017-07-03 06:36:07 -0700155 dev->features |= NETIF_F_ALL_TSO;
Michał Mirosław34324dc2011-11-15 15:29:55 +0000156 dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
Eric Dumazet8f3af272015-10-19 20:17:59 -0700157 dev->features |= NETIF_F_GSO_ENCAP_ALL;
158 dev->hw_features |= dev->features;
159 dev->hw_enc_features |= dev->features;
Danny Kukawka7ce5d222012-02-15 06:45:40 +0000160 eth_hw_addr_random(dev);
Zhang Shengju25e3e842016-12-07 17:41:33 +0800161
162 dev->min_mtu = 0;
Zhang Shengjue94cd812017-09-22 23:57:49 +0800163 dev->max_mtu = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
Eric Dumazet6d81f412010-09-27 20:50:33 +0000165
Matthias Schiffera8b8a8892017-06-25 23:56:01 +0200166static int dummy_validate(struct nlattr *tb[], struct nlattr *data[],
167 struct netlink_ext_ack *extack)
Patrick McHardy0e068772007-07-11 19:42:31 -0700168{
169 if (tb[IFLA_ADDRESS]) {
170 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
171 return -EINVAL;
172 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
173 return -EADDRNOTAVAIL;
174 }
175 return 0;
176}
177
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700178static struct rtnl_link_ops dummy_link_ops __read_mostly = {
Flavio Leitnerc19be732014-12-05 22:13:24 -0200179 .kind = DRV_NAME,
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700180 .setup = dummy_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700181 .validate = dummy_validate,
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700182};
183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184/* Number of dummy devices to be set up by this module. */
185module_param(numdummies, int, 0);
186MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices");
187
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700188static int __init dummy_init_one(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 struct net_device *dev_dummy;
191 int err;
192
Jakub Kicinskic3361612017-12-01 15:09:02 -0800193 dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 if (!dev_dummy)
195 return -ENOMEM;
196
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700197 dev_dummy->rtnl_link_ops = &dummy_link_ops;
198 err = register_netdevice(dev_dummy);
199 if (err < 0)
200 goto err;
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700201 return 0;
202
203err:
204 free_netdev(dev_dummy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 return err;
206}
207
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208static int __init dummy_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400209{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 int i, err = 0;
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700211
Kirill Tkhai554873e2018-03-30 19:38:37 +0300212 down_write(&pernet_ops_rwsem);
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700213 rtnl_lock();
214 err = __rtnl_link_register(&dummy_link_ops);
dingtianhong2c8a0182013-07-11 19:04:02 +0800215 if (err < 0)
216 goto out;
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700217
Eric Dumazet16b0dc22012-06-10 21:11:57 +0000218 for (i = 0; i < numdummies && !err; i++) {
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700219 err = dummy_init_one();
Eric Dumazet16b0dc22012-06-10 21:11:57 +0000220 cond_resched();
221 }
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700222 if (err < 0)
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700223 __rtnl_link_unregister(&dummy_link_ops);
dingtianhong2c8a0182013-07-11 19:04:02 +0800224
225out:
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700226 rtnl_unlock();
Kirill Tkhai554873e2018-03-30 19:38:37 +0300227 up_write(&pernet_ops_rwsem);
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700228
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400230}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
232static void __exit dummy_cleanup_module(void)
233{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700234 rtnl_link_unregister(&dummy_link_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235}
236
237module_init(dummy_init_module);
238module_exit(dummy_cleanup_module);
239MODULE_LICENSE("GPL");
Flavio Leitnerc19be732014-12-05 22:13:24 -0200240MODULE_ALIAS_RTNL_LINK(DRV_NAME);
Flavio Leitner6c702fa2014-12-09 22:41:48 -0200241MODULE_VERSION(DRV_VERSION);