| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* 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 Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 14 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 | [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 Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | #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> | 
|  | 37 |  | 
|  | 38 | static int numdummies = 1; | 
|  | 39 |  | 
|  | 40 | static int dummy_xmit(struct sk_buff *skb, struct net_device *dev); | 
|  | 41 | static struct net_device_stats *dummy_get_stats(struct net_device *dev); | 
|  | 42 |  | 
|  | 43 | static int dummy_set_address(struct net_device *dev, void *p) | 
|  | 44 | { | 
|  | 45 | struct sockaddr *sa = p; | 
|  | 46 |  | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 47 | if (!is_valid_ether_addr(sa->sa_data)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | return -EADDRNOTAVAIL; | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 49 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 | memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); | 
|  | 51 | return 0; | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | /* fake multicast ability */ | 
|  | 55 | static void set_multicast_list(struct net_device *dev) | 
|  | 56 | { | 
|  | 57 | } | 
|  | 58 |  | 
|  | 59 | static void __init dummy_setup(struct net_device *dev) | 
|  | 60 | { | 
|  | 61 | /* Initialize the device structure. */ | 
|  | 62 | dev->get_stats = dummy_get_stats; | 
|  | 63 | dev->hard_start_xmit = dummy_xmit; | 
|  | 64 | dev->set_multicast_list = set_multicast_list; | 
|  | 65 | dev->set_mac_address = dummy_set_address; | 
|  | 66 |  | 
|  | 67 | /* Fill in device structure with ethernet-generic values. */ | 
|  | 68 | ether_setup(dev); | 
|  | 69 | dev->tx_queue_len = 0; | 
|  | 70 | dev->change_mtu = NULL; | 
|  | 71 | dev->flags |= IFF_NOARP; | 
|  | 72 | dev->flags &= ~IFF_MULTICAST; | 
|  | 73 | SET_MODULE_OWNER(dev); | 
|  | 74 | random_ether_addr(dev->dev_addr); | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | static int dummy_xmit(struct sk_buff *skb, struct net_device *dev) | 
|  | 78 | { | 
|  | 79 | struct net_device_stats *stats = netdev_priv(dev); | 
|  | 80 |  | 
|  | 81 | stats->tx_packets++; | 
|  | 82 | stats->tx_bytes+=skb->len; | 
|  | 83 |  | 
|  | 84 | dev_kfree_skb(skb); | 
|  | 85 | return 0; | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | static struct net_device_stats *dummy_get_stats(struct net_device *dev) | 
|  | 89 | { | 
|  | 90 | return netdev_priv(dev); | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | static struct net_device **dummies; | 
|  | 94 |  | 
|  | 95 | /* Number of dummy devices to be set up by this module. */ | 
|  | 96 | module_param(numdummies, int, 0); | 
|  | 97 | MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices"); | 
|  | 98 |  | 
|  | 99 | static int __init dummy_init_one(int index) | 
|  | 100 | { | 
|  | 101 | struct net_device *dev_dummy; | 
|  | 102 | int err; | 
|  | 103 |  | 
|  | 104 | dev_dummy = alloc_netdev(sizeof(struct net_device_stats), | 
|  | 105 | "dummy%d", dummy_setup); | 
|  | 106 |  | 
|  | 107 | if (!dev_dummy) | 
|  | 108 | return -ENOMEM; | 
|  | 109 |  | 
|  | 110 | if ((err = register_netdev(dev_dummy))) { | 
|  | 111 | free_netdev(dev_dummy); | 
|  | 112 | dev_dummy = NULL; | 
|  | 113 | } else { | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 114 | dummies[index] = dev_dummy; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | } | 
|  | 116 |  | 
|  | 117 | return err; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | static void dummy_free_one(int index) | 
|  | 121 | { | 
|  | 122 | unregister_netdev(dummies[index]); | 
|  | 123 | free_netdev(dummies[index]); | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 124 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 |  | 
|  | 126 | static int __init dummy_init_module(void) | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 127 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 128 | int i, err = 0; | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 129 | dummies = kmalloc(numdummies * sizeof(void *), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | if (!dummies) | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 131 | return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | for (i = 0; i < numdummies && !err; i++) | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 133 | err = dummy_init_one(i); | 
|  | 134 | if (err) { | 
| Nicolas Dichtel | 9ed3627 | 2006-07-21 15:09:07 -0700 | [diff] [blame] | 135 | i--; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 | while (--i >= 0) | 
|  | 137 | dummy_free_one(i); | 
|  | 138 | } | 
|  | 139 | return err; | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 140 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 141 |  | 
|  | 142 | static void __exit dummy_cleanup_module(void) | 
|  | 143 | { | 
|  | 144 | int i; | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 145 | for (i = 0; i < numdummies; i++) | 
|  | 146 | dummy_free_one(i); | 
|  | 147 | kfree(dummies); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 148 | } | 
|  | 149 |  | 
|  | 150 | module_init(dummy_init_module); | 
|  | 151 | module_exit(dummy_cleanup_module); | 
|  | 152 | MODULE_LICENSE("GPL"); |