blob: bab3a9bb5e6f0c7d0f83fd19de68f7b56166fd95 [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"
Flavio Leitnerc19be732014-12-05 22:13:24 -020045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046static int numdummies = 1;
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048/* fake multicast ability */
49static void set_multicast_list(struct net_device *dev)
50{
51}
52
stephen hemmingerbc1f4472017-01-06 19:12:52 -080053static void dummy_get_stats64(struct net_device *dev,
54 struct rtnl_link_stats64 *stats)
Eric Dumazet6d81f412010-09-27 20:50:33 +000055{
Eric Dumazet4a43b1f2019-11-07 16:27:19 -080056 dev_lstats_read(dev, &stats->tx_packets, &stats->tx_bytes);
Eric Dumazet6d81f412010-09-27 20:50:33 +000057}
Stephen Hemminger424efe92009-08-31 19:50:51 +000058
59static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
60{
Eric Dumazet4a43b1f2019-11-07 16:27:19 -080061 dev_lstats_add(dev, skb->len);
Stephen Hemminger424efe92009-08-31 19:50:51 +000062
Ezequiel Lara Gomez6df014c2017-03-11 20:06:54 +000063 skb_tx_timestamp(skb);
Stephen Hemminger424efe92009-08-31 19:50:51 +000064 dev_kfree_skb(skb);
65 return NETDEV_TX_OK;
66}
67
Eric Dumazet6d81f412010-09-27 20:50:33 +000068static int dummy_dev_init(struct net_device *dev)
69{
Eric Dumazet4a43b1f2019-11-07 16:27:19 -080070 dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
71 if (!dev->lstats)
Eric Dumazet6d81f412010-09-27 20:50:33 +000072 return -ENOMEM;
73
74 return 0;
75}
76
Hiroaki SHIMODA890fdf22012-04-15 13:26:01 +000077static void dummy_dev_uninit(struct net_device *dev)
Eric Dumazet6d81f412010-09-27 20:50:33 +000078{
Eric Dumazet4a43b1f2019-11-07 16:27:19 -080079 free_percpu(dev->lstats);
Eric Dumazet6d81f412010-09-27 20:50:33 +000080}
81
Jiri Pirko210ab662012-12-27 23:49:40 +000082static int dummy_change_carrier(struct net_device *dev, bool new_carrier)
83{
84 if (new_carrier)
85 netif_carrier_on(dev);
86 else
87 netif_carrier_off(dev);
88 return 0;
89}
90
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -080091static const struct net_device_ops dummy_netdev_ops = {
Eric Dumazet6d81f412010-09-27 20:50:33 +000092 .ndo_init = dummy_dev_init,
Hiroaki SHIMODA890fdf22012-04-15 13:26:01 +000093 .ndo_uninit = dummy_dev_uninit,
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -080094 .ndo_start_xmit = dummy_xmit,
95 .ndo_validate_addr = eth_validate_addr,
Jiri Pirkoafc4b132011-08-16 06:29:01 +000096 .ndo_set_rx_mode = set_multicast_list,
Jiri Pirko0d1632b2012-06-29 05:10:08 +000097 .ndo_set_mac_address = eth_mac_addr,
Eric Dumazet6d81f412010-09-27 20:50:33 +000098 .ndo_get_stats64 = dummy_get_stats64,
Jiri Pirko210ab662012-12-27 23:49:40 +000099 .ndo_change_carrier = dummy_change_carrier,
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800100};
101
Flavio Leitnerc19be732014-12-05 22:13:24 -0200102static void dummy_get_drvinfo(struct net_device *dev,
103 struct ethtool_drvinfo *info)
104{
105 strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
Flavio Leitnerc19be732014-12-05 22:13:24 -0200106}
107
108static const struct ethtool_ops dummy_ethtool_ops = {
109 .get_drvinfo = dummy_get_drvinfo,
Julian Wiedmannabe9fd52019-04-12 13:06:13 +0200110 .get_ts_info = ethtool_op_get_ts_info,
Flavio Leitnerc19be732014-12-05 22:13:24 -0200111};
112
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700113static void dummy_setup(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800115 ether_setup(dev);
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 /* Initialize the device structure. */
Stephen Hemmingeraa18e9e2008-11-20 20:28:00 -0800118 dev->netdev_ops = &dummy_netdev_ops;
Flavio Leitnerc19be732014-12-05 22:13:24 -0200119 dev->ethtool_ops = &dummy_ethtool_ops;
David S. Millercf124db2017-05-08 12:52:56 -0400120 dev->needs_free_netdev = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
122 /* Fill in device structure with ethernet-generic values. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 dev->flags |= IFF_NOARP;
124 dev->flags &= ~IFF_MULTICAST;
Phil Sutterff42c022015-08-18 10:30:30 +0200125 dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
Eric Dumazet8f3af272015-10-19 20:17:59 -0700126 dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST;
David S. Miller20824992017-07-03 06:36:07 -0700127 dev->features |= NETIF_F_ALL_TSO;
Michał Mirosław34324dc2011-11-15 15:29:55 +0000128 dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
Eric Dumazet8f3af272015-10-19 20:17:59 -0700129 dev->features |= NETIF_F_GSO_ENCAP_ALL;
130 dev->hw_features |= dev->features;
131 dev->hw_enc_features |= dev->features;
Danny Kukawka7ce5d222012-02-15 06:45:40 +0000132 eth_hw_addr_random(dev);
Zhang Shengju25e3e842016-12-07 17:41:33 +0800133
134 dev->min_mtu = 0;
Zhang Shengjue94cd812017-09-22 23:57:49 +0800135 dev->max_mtu = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
Eric Dumazet6d81f412010-09-27 20:50:33 +0000137
Matthias Schiffera8b8a8892017-06-25 23:56:01 +0200138static int dummy_validate(struct nlattr *tb[], struct nlattr *data[],
139 struct netlink_ext_ack *extack)
Patrick McHardy0e068772007-07-11 19:42:31 -0700140{
141 if (tb[IFLA_ADDRESS]) {
142 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
143 return -EINVAL;
144 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
145 return -EADDRNOTAVAIL;
146 }
147 return 0;
148}
149
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700150static struct rtnl_link_ops dummy_link_ops __read_mostly = {
Flavio Leitnerc19be732014-12-05 22:13:24 -0200151 .kind = DRV_NAME,
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700152 .setup = dummy_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700153 .validate = dummy_validate,
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700154};
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156/* Number of dummy devices to be set up by this module. */
157module_param(numdummies, int, 0);
158MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices");
159
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700160static int __init dummy_init_one(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
162 struct net_device *dev_dummy;
163 int err;
164
Jakub Kicinskic3361612017-12-01 15:09:02 -0800165 dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (!dev_dummy)
167 return -ENOMEM;
168
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700169 dev_dummy->rtnl_link_ops = &dummy_link_ops;
170 err = register_netdevice(dev_dummy);
171 if (err < 0)
172 goto err;
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700173 return 0;
174
175err:
176 free_netdev(dev_dummy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return err;
178}
179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180static int __init dummy_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400181{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 int i, err = 0;
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700183
Kirill Tkhai554873e2018-03-30 19:38:37 +0300184 down_write(&pernet_ops_rwsem);
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700185 rtnl_lock();
186 err = __rtnl_link_register(&dummy_link_ops);
dingtianhong2c8a0182013-07-11 19:04:02 +0800187 if (err < 0)
188 goto out;
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700189
Eric Dumazet16b0dc22012-06-10 21:11:57 +0000190 for (i = 0; i < numdummies && !err; i++) {
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700191 err = dummy_init_one();
Eric Dumazet16b0dc22012-06-10 21:11:57 +0000192 cond_resched();
193 }
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700194 if (err < 0)
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700195 __rtnl_link_unregister(&dummy_link_ops);
dingtianhong2c8a0182013-07-11 19:04:02 +0800196
197out:
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700198 rtnl_unlock();
Kirill Tkhai554873e2018-03-30 19:38:37 +0300199 up_write(&pernet_ops_rwsem);
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400202}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204static void __exit dummy_cleanup_module(void)
205{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700206 rtnl_link_unregister(&dummy_link_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
209module_init(dummy_init_module);
210module_exit(dummy_cleanup_module);
211MODULE_LICENSE("GPL");
Flavio Leitnerc19be732014-12-05 22:13:24 -0200212MODULE_ALIAS_RTNL_LINK(DRV_NAME);