blob: 91126b9ce4531d11d429039b4da2014a1ff7a517 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* dummy.c: a dummy net driver
2
3 The purpose of this driver is to provide a device to point a
4 route through, but not to actually transmit packets.
5
6 Why? If you have a machine whose only connection is an occasional
7 PPP/SLIP/PLIP link, you can only connect to your own hostname
8 when the link is up. Otherwise you have to use localhost.
9 This isn't very consistent.
10
11 One solution is to set up a dummy link using PPP/SLIP/PLIP,
12 but this seems (to me) too much overhead for too little gain.
13 This driver provides a small alternative. Thus you can do
Jeff Garzik6aa20a22006-09-13 13:24:59 -040014
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 [when not running slip]
16 ifconfig dummy slip.addr.ess.here up
17 [to go to slip]
18 ifconfig dummy down
19 dip whatever
20
21 This was written by looking at Donald Becker's skeleton driver
22 and the loopback driver. I then threw away anything that didn't
23 apply! Thanks to Alan Cox for the key clue on what to do with
24 misguided packets.
25
26 Nick Holloway, 27th May 1994
27 [I tweaked this explanation a little but that's all]
28 Alan Cox, 30th May 1994
29*/
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/module.h>
32#include <linux/kernel.h>
33#include <linux/netdevice.h>
34#include <linux/etherdevice.h>
35#include <linux/init.h>
36#include <linux/moduleparam.h>
Patrick McHardy206c9fb2007-06-13 12:04:20 -070037#include <linux/rtnetlink.h>
Patrick McHardy5d5cb172007-06-13 12:04:34 -070038#include <net/rtnetlink.h>
Patrick McHardy206c9fb2007-06-13 12:04:20 -070039
40struct dummy_priv {
41 struct net_device *dev;
42 struct list_head list;
43};
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45static int numdummies = 1;
46
47static int dummy_xmit(struct sk_buff *skb, struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49static int dummy_set_address(struct net_device *dev, void *p)
50{
51 struct sockaddr *sa = p;
52
Jeff Garzik6aa20a22006-09-13 13:24:59 -040053 if (!is_valid_ether_addr(sa->sa_data))
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 return -EADDRNOTAVAIL;
Jeff Garzik6aa20a22006-09-13 13:24:59 -040055
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
57 return 0;
58}
59
60/* fake multicast ability */
61static void set_multicast_list(struct net_device *dev)
62{
63}
64
Patrick McHardy5d5cb172007-06-13 12:04:34 -070065static void dummy_setup(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
67 /* Initialize the device structure. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 dev->hard_start_xmit = dummy_xmit;
69 dev->set_multicast_list = set_multicast_list;
70 dev->set_mac_address = dummy_set_address;
Patrick McHardy5d5cb172007-06-13 12:04:34 -070071 dev->destructor = free_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73 /* Fill in device structure with ethernet-generic values. */
74 ether_setup(dev);
75 dev->tx_queue_len = 0;
76 dev->change_mtu = NULL;
77 dev->flags |= IFF_NOARP;
78 dev->flags &= ~IFF_MULTICAST;
79 SET_MODULE_OWNER(dev);
80 random_ether_addr(dev->dev_addr);
81}
82
83static int dummy_xmit(struct sk_buff *skb, struct net_device *dev)
84{
Patrick McHardy58651b22007-06-13 12:04:06 -070085 dev->stats.tx_packets++;
86 dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
88 dev_kfree_skb(skb);
89 return 0;
90}
91
Patrick McHardy206c9fb2007-06-13 12:04:20 -070092static LIST_HEAD(dummies);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Patrick McHardy5d5cb172007-06-13 12:04:34 -070094static int dummy_newlink(struct net_device *dev,
95 struct nlattr *tb[], struct nlattr *data[])
96{
97 struct dummy_priv *priv = netdev_priv(dev);
98 int err;
99
100 err = register_netdevice(dev);
101 if (err < 0)
102 return err;
103
104 priv->dev = dev;
105 list_add_tail(&priv->list, &dummies);
106 return 0;
107}
108
109static void dummy_dellink(struct net_device *dev)
110{
111 struct dummy_priv *priv = netdev_priv(dev);
112
113 list_del(&priv->list);
114 unregister_netdevice(dev);
115}
116
117static struct rtnl_link_ops dummy_link_ops __read_mostly = {
118 .kind = "dummy",
119 .priv_size = sizeof(struct dummy_priv),
120 .setup = dummy_setup,
121 .newlink = dummy_newlink,
122 .dellink = dummy_dellink,
123};
124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125/* Number of dummy devices to be set up by this module. */
126module_param(numdummies, int, 0);
127MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices");
128
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700129static int __init dummy_init_one(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
131 struct net_device *dev_dummy;
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700132 struct dummy_priv *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 int err;
134
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700135 dev_dummy = alloc_netdev(sizeof(struct dummy_priv), "dummy%d",
136 dummy_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
138 if (!dev_dummy)
139 return -ENOMEM;
140
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700141 err = dev_alloc_name(dev_dummy, dev_dummy->name);
142 if (err < 0)
143 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700145 dev_dummy->rtnl_link_ops = &dummy_link_ops;
146 err = register_netdevice(dev_dummy);
147 if (err < 0)
148 goto err;
149
150 priv = netdev_priv(dev_dummy);
151 priv->dev = dev_dummy;
152 list_add_tail(&priv->list, &dummies);
153 return 0;
154
155err:
156 free_netdev(dev_dummy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return err;
158}
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160static int __init dummy_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400161{
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700162 struct dummy_priv *priv, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 int i, err = 0;
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700164
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700165 rtnl_lock();
166 err = __rtnl_link_register(&dummy_link_ops);
167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 for (i = 0; i < numdummies && !err; i++)
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700169 err = dummy_init_one();
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700170 if (err < 0) {
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700171 list_for_each_entry_safe(priv, next, &dummies, list)
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700172 dummy_dellink(priv->dev);
173 __rtnl_link_unregister(&dummy_link_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 }
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700175 rtnl_unlock();
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400178}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180static void __exit dummy_cleanup_module(void)
181{
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700182 struct dummy_priv *priv, *next;
183
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700184 rtnl_lock();
Patrick McHardy206c9fb2007-06-13 12:04:20 -0700185 list_for_each_entry_safe(priv, next, &dummies, list)
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700186 dummy_dellink(priv->dev);
187
188 __rtnl_link_unregister(&dummy_link_ops);
189 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
192module_init(dummy_init_module);
193module_exit(dummy_cleanup_module);
194MODULE_LICENSE("GPL");
Patrick McHardy5d5cb172007-06-13 12:04:34 -0700195MODULE_ALIAS_RTNL_LINK("dummy");