162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* dummy.c: a dummy net driver 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci The purpose of this driver is to provide a device to point a 562306a36Sopenharmony_ci route through, but not to actually transmit packets. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Why? If you have a machine whose only connection is an occasional 862306a36Sopenharmony_ci PPP/SLIP/PLIP link, you can only connect to your own hostname 962306a36Sopenharmony_ci when the link is up. Otherwise you have to use localhost. 1062306a36Sopenharmony_ci This isn't very consistent. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci One solution is to set up a dummy link using PPP/SLIP/PLIP, 1362306a36Sopenharmony_ci but this seems (to me) too much overhead for too little gain. 1462306a36Sopenharmony_ci This driver provides a small alternative. Thus you can do 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci [when not running slip] 1762306a36Sopenharmony_ci ifconfig dummy slip.addr.ess.here up 1862306a36Sopenharmony_ci [to go to slip] 1962306a36Sopenharmony_ci ifconfig dummy down 2062306a36Sopenharmony_ci dip whatever 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci This was written by looking at Donald Becker's skeleton driver 2362306a36Sopenharmony_ci and the loopback driver. I then threw away anything that didn't 2462306a36Sopenharmony_ci apply! Thanks to Alan Cox for the key clue on what to do with 2562306a36Sopenharmony_ci misguided packets. 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci Nick Holloway, 27th May 1994 2862306a36Sopenharmony_ci [I tweaked this explanation a little but that's all] 2962306a36Sopenharmony_ci Alan Cox, 30th May 1994 3062306a36Sopenharmony_ci*/ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci#include <linux/kernel.h> 3462306a36Sopenharmony_ci#include <linux/netdevice.h> 3562306a36Sopenharmony_ci#include <linux/etherdevice.h> 3662306a36Sopenharmony_ci#include <linux/ethtool.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/moduleparam.h> 3962306a36Sopenharmony_ci#include <linux/rtnetlink.h> 4062306a36Sopenharmony_ci#include <linux/net_tstamp.h> 4162306a36Sopenharmony_ci#include <net/rtnetlink.h> 4262306a36Sopenharmony_ci#include <linux/u64_stats_sync.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define DRV_NAME "dummy" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int numdummies = 1; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* fake multicast ability */ 4962306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void dummy_get_stats64(struct net_device *dev, 5462306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci dev_lstats_read(dev, &stats->tx_packets, &stats->tx_bytes); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci dev_lstats_add(dev, skb->len); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci skb_tx_timestamp(skb); 6462306a36Sopenharmony_ci dev_kfree_skb(skb); 6562306a36Sopenharmony_ci return NETDEV_TX_OK; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int dummy_dev_init(struct net_device *dev) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); 7162306a36Sopenharmony_ci if (!dev->lstats) 7262306a36Sopenharmony_ci return -ENOMEM; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void dummy_dev_uninit(struct net_device *dev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci free_percpu(dev->lstats); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int dummy_change_carrier(struct net_device *dev, bool new_carrier) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (new_carrier) 8562306a36Sopenharmony_ci netif_carrier_on(dev); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci netif_carrier_off(dev); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const struct net_device_ops dummy_netdev_ops = { 9262306a36Sopenharmony_ci .ndo_init = dummy_dev_init, 9362306a36Sopenharmony_ci .ndo_uninit = dummy_dev_uninit, 9462306a36Sopenharmony_ci .ndo_start_xmit = dummy_xmit, 9562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 9662306a36Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 9762306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 9862306a36Sopenharmony_ci .ndo_get_stats64 = dummy_get_stats64, 9962306a36Sopenharmony_ci .ndo_change_carrier = dummy_change_carrier, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const struct ethtool_ops dummy_ethtool_ops = { 10362306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void dummy_setup(struct net_device *dev) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci ether_setup(dev); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Initialize the device structure. */ 11162306a36Sopenharmony_ci dev->netdev_ops = &dummy_netdev_ops; 11262306a36Sopenharmony_ci dev->ethtool_ops = &dummy_ethtool_ops; 11362306a36Sopenharmony_ci dev->needs_free_netdev = true; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Fill in device structure with ethernet-generic values. */ 11662306a36Sopenharmony_ci dev->flags |= IFF_NOARP; 11762306a36Sopenharmony_ci dev->flags &= ~IFF_MULTICAST; 11862306a36Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; 11962306a36Sopenharmony_ci dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; 12062306a36Sopenharmony_ci dev->features |= NETIF_F_GSO_SOFTWARE; 12162306a36Sopenharmony_ci dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX; 12262306a36Sopenharmony_ci dev->features |= NETIF_F_GSO_ENCAP_ALL; 12362306a36Sopenharmony_ci dev->hw_features |= dev->features; 12462306a36Sopenharmony_ci dev->hw_enc_features |= dev->features; 12562306a36Sopenharmony_ci eth_hw_addr_random(dev); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci dev->min_mtu = 0; 12862306a36Sopenharmony_ci dev->max_mtu = 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int dummy_validate(struct nlattr *tb[], struct nlattr *data[], 13262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (tb[IFLA_ADDRESS]) { 13562306a36Sopenharmony_ci if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) 13862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct rtnl_link_ops dummy_link_ops __read_mostly = { 14462306a36Sopenharmony_ci .kind = DRV_NAME, 14562306a36Sopenharmony_ci .setup = dummy_setup, 14662306a36Sopenharmony_ci .validate = dummy_validate, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Number of dummy devices to be set up by this module. */ 15062306a36Sopenharmony_cimodule_param(numdummies, int, 0); 15162306a36Sopenharmony_ciMODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices"); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int __init dummy_init_one(void) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct net_device *dev_dummy; 15662306a36Sopenharmony_ci int err; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup); 15962306a36Sopenharmony_ci if (!dev_dummy) 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dev_dummy->rtnl_link_ops = &dummy_link_ops; 16362306a36Sopenharmony_ci err = register_netdevice(dev_dummy); 16462306a36Sopenharmony_ci if (err < 0) 16562306a36Sopenharmony_ci goto err; 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cierr: 16962306a36Sopenharmony_ci free_netdev(dev_dummy); 17062306a36Sopenharmony_ci return err; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int __init dummy_init_module(void) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int i, err = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci down_write(&pernet_ops_rwsem); 17862306a36Sopenharmony_ci rtnl_lock(); 17962306a36Sopenharmony_ci err = __rtnl_link_register(&dummy_link_ops); 18062306a36Sopenharmony_ci if (err < 0) 18162306a36Sopenharmony_ci goto out; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < numdummies && !err; i++) { 18462306a36Sopenharmony_ci err = dummy_init_one(); 18562306a36Sopenharmony_ci cond_resched(); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci if (err < 0) 18862306a36Sopenharmony_ci __rtnl_link_unregister(&dummy_link_ops); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciout: 19162306a36Sopenharmony_ci rtnl_unlock(); 19262306a36Sopenharmony_ci up_write(&pernet_ops_rwsem); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return err; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void __exit dummy_cleanup_module(void) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci rtnl_link_unregister(&dummy_link_ops); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cimodule_init(dummy_init_module); 20362306a36Sopenharmony_cimodule_exit(dummy_cleanup_module); 20462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 20562306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME); 206