Why not implement a true virtual network rather than simple tunnel pairs? Details:
> +static struct { > + const char string[ETH_GSTRING_LEN]; > +} ethtool_stats_keys[ETUN_NUM_STATS] = { > + { "partner_ifindex" }, > +}; You should use sysfs attributes for this rather than ethtool. > +struct etun_info { > + struct net_device *rx_dev; > + unsigned ip_summed; > + struct net_device_stats stats; > + struct list_head list; > + struct net_device *dev; > +}; > +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 void etun_get_strings(struct net_device *dev, u32 stringset, u8 *buf) > +{ > + switch(stringset) { > + case ETH_SS_STATS: > + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); > + break; > + case ETH_SS_TEST: > + default: > + break; > + } > +} > + > +static int etun_get_stats_count(struct net_device *dev) > +{ > + return ETUN_NUM_STATS; > +} > + > +static void etun_get_ethtool_stats(struct net_device *dev, > + struct ethtool_stats *stats, u64 *data) > +{ > + struct etun_info *info = dev->priv; > + > + data[0] = info->rx_dev->ifindex; > +} > + > +static u32 etun_get_rx_csum(struct net_device *dev) > +{ > + struct etun_info *info = dev->priv; > + return info->ip_summed == CHECKSUM_UNNECESSARY; > +} > + > +static int etun_set_rx_csum(struct net_device *dev, u32 data) > +{ > + struct etun_info *info = dev->priv; > + > + info->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; > + > + return 0; > +} > + > +static u32 etun_get_tx_csum(struct net_device *dev) > +{ > + return (dev->features & NETIF_F_NO_CSUM) != 0; > +} > + > +static int etun_set_tx_csum(struct net_device *dev, u32 data) > +{ > + dev->features &= ~NETIF_F_NO_CSUM; > + if (data) > + dev->features |= NETIF_F_NO_CSUM; > + > + return 0; > +} > + > +static struct ethtool_ops etun_ethtool_ops = { > + .get_settings = etun_get_settings, > + .get_drvinfo = etun_get_drvinfo, > + .get_link = ethtool_op_get_link, > + .get_rx_csum = etun_get_rx_csum, > + .set_rx_csum = etun_set_rx_csum, > + .get_tx_csum = etun_get_tx_csum, > + .set_tx_csum = etun_set_tx_csum, > + .get_sg = ethtool_op_get_sg, > + .set_sg = ethtool_op_set_sg, > +#if 0 /* Does just setting the bit successfuly emulate tso? */ > + .get_tso = ethtool_op_get_tso, > + .set_tso = ethtool_op_set_tso, > +#endif > + .get_strings = etun_get_strings, > + .get_stats_count = etun_get_stats_count, > + .get_ethtool_stats = etun_get_ethtool_stats, > + .get_perm_addr = ethtool_op_get_perm_addr, > +}; > + > +static int etun_open(struct net_device *tx_dev) > +{ > + struct etun_info *tx_info = tx_dev->priv; > + struct net_device *rx_dev = tx_info->rx_dev; > + /* If we attempt to bring up etun in the small window before > + * it is connected to it's partner error. > + */ > + if (!rx_dev) > + return -ENOTCONN; > + if (rx_dev->flags & IFF_UP) { > + netif_carrier_on(tx_dev); > + netif_carrier_on(rx_dev); > + } > + netif_start_queue(tx_dev); > + return 0; > +} > + > +static int etun_stop(struct net_device *tx_dev) > +{ > + struct etun_info *tx_info = tx_dev->priv; > + struct net_device *rx_dev = tx_info->rx_dev; > + netif_stop_queue(tx_dev); > + if (netif_carrier_ok(tx_dev)) { > + netif_carrier_off(tx_dev); > + netif_carrier_off(rx_dev); > + } > + return 0; > +} > + > +static int etun_change_mtu(struct net_device *dev, int new_mtu) > +{ > + /* Don't allow ridiculously small mtus */ > + if (new_mtu < (ETH_ZLEN - ETH_HLEN)) > + return -EINVAL; > + dev->mtu = new_mtu; > + 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; > +} If not supported, then no stub needed just leave null. > +/* Only allow letters and numbers in an etun device name */ > +static int is_valid_name(const char *name) > +{ > + const char *ptr; > + for (ptr = name; *ptr; ptr++) { > + if (!isalnum(*ptr)) > + return 0; > + } > + return 1; > +} > + > +static struct net_device *etun_alloc(const char *name) > +{ > + struct net_device *dev; > + struct etun_info *info; > + int err; > + > + if (!name || !is_valid_name(name)) > + return ERR_PTR(-EINVAL); > + > + dev = alloc_netdev(sizeof(struct etun_info), name, ether_setup); > + if (!dev) > + return ERR_PTR(-ENOMEM); Use alloc_etherdev() instead. > + info = dev->priv; > + info->dev = dev; > + > + 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_FRAGLIST > + | NETIF_F_HIGHDMA > + | NETIF_F_LLTX; > + dev->flags = IFF_BROADCAST | IFF_MULTICAST |IFF_PROMISC; > + dev->ethtool_ops = &etun_ethtool_ops; > + dev->destructor = free_netdev; > + dev->change_mtu = etun_change_mtu; > + err = register_netdev(dev); > + if (err) { > + free_netdev(dev); > + dev = ERR_PTR(err); > + goto out; > + } > + netif_carrier_off(dev); > +out: > + return dev; > +} > + > +static int etun_alloc_pair(const char *name0, const char *name1) > +{ > + struct net_device *dev0, *dev1; > + struct etun_info *info0, *info1; > + > + dev0 = etun_alloc(name0); > + if (IS_ERR(dev0)) { > + return PTR_ERR(dev0); > + } > + info0 = dev0->priv; > + > + dev1 = etun_alloc(name1); > + if (IS_ERR(dev1)) { > + unregister_netdev(dev0); > + return PTR_ERR(dev1); > + } > + info1 = dev1->priv; > + > + dev_hold(dev0); > + dev_hold(dev1); > + info0->rx_dev = dev1; > + info1->rx_dev = dev0; > + > + /* Only place one member of the pair on the list > + * so I don't confuse list_for_each_entry_safe, > + * by deleting two list entries at once. > + */ > + rtnl_lock(); > + list_add(&info0->list, &etun_list); > + INIT_LIST_HEAD(&info1->list); > + rtnl_unlock(); > + > + return 0; > +} > + > +static int etun_unregister_pair(struct net_device *dev0) > +{ > + struct etun_info *info0, *info1; > + struct net_device *dev1; > + > + ASSERT_RTNL(); > + > + if (!dev0) > + return -ENODEV; > + > + /* Ensure my network devices are not passing packets */ > + dev_close(dev0); > + info0 = dev0->priv; > + dev1 = info0->rx_dev; > + info1 = dev1->priv; > + dev_close(dev1); > + > + /* Drop the cross device references */ > + dev_put(dev0); > + dev_put(dev1); > + > + /* Remove from the etun list */ > + if (!list_empty(&info0->list)) > + list_del_init(&info0->list); > + if (!list_empty(&info1->list)) > + list_del_init(&info1->list); > + > + unregister_netdevice(dev0); > + unregister_netdevice(dev1); > + return 0; > +} > + > +static int etun_noget(char *buffer, struct kernel_param *kp) > +{ > + return 0; > +} > + > +static int etun_newif(const char *val, struct kernel_param *kp) > +{ > + char name0[IFNAMSIZ], name1[IFNAMSIZ]; > + const char *mid; > + int len, len0, len1; > + if (!capable(CAP_NET_ADMIN)) > + return -EPERM; > + > + /* Avoid frustration by removing trailing whitespace */ > + len = strlen(val); > + while (isspace(val[len - 1])) > + len--; > + > + /* Split the string into 2 names */ > + mid = memchr(val, ',', len); > + if (!mid) > + return -EINVAL; > + > + /* Get the first device name */ > + len0 = mid - val; > + if (len0 > sizeof(name0) - 1) > + len = sizeof(name0) - 1; > + strncpy(name0, val, len0); > + name0[len0] = '\0'; > + > + /* And the second device name */ > + len1 = len - (len0 + 1); > + if (len1 > sizeof(name1) - 1) > + len1 = sizeof(name1) - 1; > + strncpy(name1, mid + 1, len1); > + name1[len1] = '\0'; > + > + return etun_alloc_pair(name0, name1); > +} > + > +static int etun_delif(const char *val, struct kernel_param *kp) > +{ > + char name[IFNAMSIZ]; > + int len; > + struct net_device *dev; > + int err; > + if (!capable(CAP_NET_ADMIN)) > + return -EPERM; > + > + /* Avoid frustration by removing trailing whitespace */ > + len = strlen(val); > + while (isspace(val[len - 1])) > + len--; > + > + /* Get the device name */ > + if (len > sizeof(name) - 1) > + return -EINVAL; > + strncpy(name, val, len); > + name[len] = '\0'; > + > + /* Double check I don't have strange characters in my device name */ > + if (!is_valid_name(name)) > + return -EINVAL; > + > + rtnl_lock(); > + err = -ENODEV; > + dev = __dev_get_by_name(name); > + err = etun_unregister_pair(dev); > + rtnl_unlock(); > + return err; > +} Doing create/delete via module parameter is wierd. Why not either use an ioctl, or sysfs. > +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 0; Why bother it is just advertising noise... > +} > + > +static void etun_cleanup(void) > +{ > + struct etun_info *info, *tmp; > + rtnl_lock(); > + list_for_each_entry_safe(info, tmp, &etun_list, list) { > + etun_unregister_pair(info->dev); > + } > + rtnl_unlock(); > +} > + > +module_param_call(newif, etun_newif, etun_noget, NULL, S_IWUSR); > +module_param_call(delif, etun_delif, etun_noget, NULL, S_IWUSR); > +module_init(etun_init); > +module_exit(etun_cleanup); > +MODULE_DESCRIPTION(DRV_DESCRIPTION); > +MODULE_AUTHOR("Eric Biederman <[EMAIL PROTECTED]>"); > +MODULE_LICENSE("GPL"); - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html