Subject: [PATCH] etun: Add a loopback ethernet tunnel device. From: Eric W. Biederman Date: 1135188381 -0700 Eventually I need to update this to allocate more dynamic devices on the fly but for now a single pair of devices is enough. When working with virtual networking this allows packets to transfer between the virtual machine and the real one. etun is built so it can work with ethernet bridging as well as simply being a in box conduit of packets. This allows virtualized networks to transparently show up on the real network. Unfortunantely this means that several in box tunnel optimizations like avoiding checksums, and arp cannot be applied. As things would break when bridged to a real ethernet network. --- drivers/net/Kconfig | 13 +++ drivers/net/Makefile | 1 drivers/net/etun.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 0 deletions(-) create mode 100644 drivers/net/etun.c 2ab7ad9b5a82f7c34b323b4f82e705bff3e24413 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c748b0e..4975a1f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -105,6 +105,19 @@ config TUN If you don't know what to use this for, you don't need it. +config ETUN + tristate "Universal ETUN device driver support" + ---help--- + ETUN provices a pair of network devices that can be used for + configuring interesting topolgies. What one devices transmits + the other receives and vice versa. The link level framing + is ethernet for wide compatibility with network stacks. + + To compile this driver as a module, choose M here: the module + will be called etun. + + If you don't know what to use this for, you don't need it. + config NET_SB1000 tristate "General Instruments Surfboard 1000" depends on PNP diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 8aeec9f..e9a7e95 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -179,6 +179,7 @@ obj-$(CONFIG_MACSONIC) += macsonic.o obj-$(CONFIG_MACMACE) += macmace.o obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_TUN) += tun.o +obj-$(CONFIG_ETUN) += etun.o obj-$(CONFIG_DL2K) += dl2k.o obj-$(CONFIG_R8169) += r8169.o obj-$(CONFIG_AMD8111_ETH) += amd8111e.o diff --git a/drivers/net/etun.c b/drivers/net/etun.c new file mode 100644 index 0000000..398fbd0 --- /dev/null +++ b/drivers/net/etun.c @@ -0,0 +1,220 @@ +/* + * ETUN - Universal ETUN device driver. + * Copyright (C) 2005 Eric Biederman + * + */ + +#define DRV_NAME "etun" +#define DRV_VERSION "1.0" +#define DRV_DESCRIPTION "Universal ETUN device driver" +#define DRV_COPYRIGHT "(C) 2005 Eric Biederman " + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* I believe the possibility of briding precludes the disabling + * checksum checking, on an internal interface. + * - If my source is an external interface the checksum may be + * invalid so I don't want to report I have already checked it. + * - If my destination is an external interface I don't want to + * put a packet on the write with noone computing the checksum. + */ +#define ETUN_MUST_CHECKSUM 1 + +#define MAX_ETUN 2 +static struct net_device *etun[MAX_ETUN]; + +struct etun_info { + struct net_device *rx_dev; + struct net_device_stats stats; +}; + +/* + * The higher levels take care of making this non-reentrant (it's + * called with bh's disabled). + */ +static int etun_xmit(struct sk_buff *skb, struct net_device *tx_dev) +{ + struct etun_info *tx_info = tx_dev->priv; + struct net_device *rx_dev = tx_info->rx_dev; + struct etun_info *rx_info = rx_dev->priv; + + tx_info->stats.tx_packets++; + tx_info->stats.tx_bytes += skb->len; + + /* Drop the skb state that was needed to get here */ + skb_orphan(skb); + if (skb->dst) + skb->dst = dst_pop(skb->dst); /* Allow for smart routing */ + + /* Switch to the receiving device */ + skb->protocol = eth_type_trans(skb, rx_dev); + skb->dev = rx_dev; + +#ifndef ETUN_MUST_CHECKSUM + /* There is not time for the checksums to go bad... */ + skb->ip_summed = CHECKSUM_UNNECESSARY; +#endif + rx_dev->last_rx = jiffies; + rx_info->stats.rx_packets++; + rx_info->stats.rx_bytes += skb->len; + netif_rx(skb); + + return 0; +} + +static struct net_device_stats *etun_get_stats(struct net_device *dev) +{ + struct etun_info *info = dev->priv; + return &info->stats; +} + +/* ethtool interface */ +static int etun_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + cmd->supported = 0; + cmd->advertising = 0; + cmd->speed = SPEED_10000; /* Memory is fast! */ + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_TP; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + return 0; +} + +static void etun_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); +} + +static u32 etun_get_link(struct net_device *tx_dev) +{ + struct etun_info *tx_info = tx_dev->priv; + struct net_device *rx_dev = tx_info->rx_dev; + return !!(rx_dev->flags & IFF_UP); + +} + +static struct ethtool_ops etun_ethtool_ops = { + .get_settings = etun_get_settings, + .get_drvinfo = etun_get_drvinfo, + .get_link = etun_get_link, +}; + +static int etun_open(struct net_device *dev) +{ + netif_start_queue(dev); + return 0; +} + +static int etun_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static void etun_set_multicast_list(struct net_device *dev) +{ + /* Nothing sane I can do here */ + return; +} +static int etun_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + return -EOPNOTSUPP; +} + +static struct net_device *etun_alloc(void) +{ + struct net_device *dev; + int err; + dev = alloc_netdev(sizeof(struct etun_info), "etun%d", ether_setup); + if (!dev) + return ERR_PTR(-ENOMEM); + random_ether_addr(dev->dev_addr); + dev->tx_queue_len = 0; /* A queue is silly for a loopback device */ + dev->hard_start_xmit = etun_xmit; + dev->get_stats = etun_get_stats; + dev->open = etun_open; + dev->stop = etun_stop; + dev->set_multicast_list = etun_set_multicast_list; + dev->do_ioctl = etun_ioctl; + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST +#ifndef ETUN_MUST_CHECKSUM + | NETIF_F_NO_CSUM +#endif + | NETIF_F_HIGHDMA + | NETIF_F_LLTX; + dev->flags = IFF_BROADCAST | IFF_MULTICAST |IFF_PROMISC; + dev->ethtool_ops = &etun_ethtool_ops; + dev->priv = netdev_priv(dev); + dev->destructor = free_netdev; + err = register_netdev(dev); + if (err) { + free_netdev(dev); + dev = ERR_PTR(err); + } + return dev; +} + +static int etun_alloc_pair(struct net_device **dev) +{ + struct net_device *etun0, *etun1; + struct etun_info *info0, *info1; + + etun0 = etun_alloc(); + if (IS_ERR(etun0)) { + return PTR_ERR(etun0); + } + info0 = etun0->priv; + + etun1 = etun_alloc(); + if (IS_ERR(etun1)) { + unregister_netdev(etun0); + return PTR_ERR(etun1); + } + info1 = etun1->priv; + + info0->rx_dev = etun1; + info1->rx_dev = etun0; + + dev[0] = etun0; + dev[1] = etun1; + return 0; +} + +static int __init etun_init(void) +{ + printk(KERN_INFO "etun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); + printk(KERN_INFO "etun: %s\n", DRV_COPYRIGHT); + + return etun_alloc_pair(etun); +} + +static void etun_cleanup(void) +{ + int i; + for(i = 0; i < MAX_ETUN; i++) { + unregister_netdev(etun[i]); + } +} + +module_init(etun_init); +module_exit(etun_cleanup); +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); -- 1.0.GIT