tun.c

来自「linux 内核源代码」· C语言 代码 · 共 913 行 · 第 1/2 页

C
913
字号
		    return tun;	}	return NULL;}static int tun_set_iff(struct file *file, struct ifreq *ifr){	struct tun_struct *tun;	struct net_device *dev;	int err;	tun = tun_get_by_name(ifr->ifr_name);	if (tun) {		if (tun->attached)			return -EBUSY;		/* Check permissions */		if (((tun->owner != -1 &&		      current->euid != tun->owner) ||		     (tun->group != -1 &&		      current->egid != tun->group)) &&		     !capable(CAP_NET_ADMIN))			return -EPERM;	}	else if (__dev_get_by_name(&init_net, ifr->ifr_name))		return -EINVAL;	else {		char *name;		unsigned long flags = 0;		err = -EINVAL;		if (!capable(CAP_NET_ADMIN))			return -EPERM;		/* Set dev type */		if (ifr->ifr_flags & IFF_TUN) {			/* TUN device */			flags |= TUN_TUN_DEV;			name = "tun%d";		} else if (ifr->ifr_flags & IFF_TAP) {			/* TAP device */			flags |= TUN_TAP_DEV;			name = "tap%d";		} else			goto failed;		if (*ifr->ifr_name)			name = ifr->ifr_name;		dev = alloc_netdev(sizeof(struct tun_struct), name,				   tun_setup);		if (!dev)			return -ENOMEM;		tun = netdev_priv(dev);		tun->dev = dev;		tun->flags = flags;		/* Be promiscuous by default to maintain previous behaviour. */		tun->if_flags = IFF_PROMISC;		/* Generate random Ethernet address. */		*(u16 *)tun->dev_addr = htons(0x00FF);		get_random_bytes(tun->dev_addr + sizeof(u16), 4);		memset(tun->chr_filter, 0, sizeof tun->chr_filter);		tun_net_init(dev);		if (strchr(dev->name, '%')) {			err = dev_alloc_name(dev, dev->name);			if (err < 0)				goto err_free_dev;		}		err = register_netdevice(tun->dev);		if (err < 0)			goto err_free_dev;		list_add(&tun->list, &tun_dev_list);	}	DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name);	if (ifr->ifr_flags & IFF_NO_PI)		tun->flags |= TUN_NO_PI;	if (ifr->ifr_flags & IFF_ONE_QUEUE)		tun->flags |= TUN_ONE_QUEUE;	file->private_data = tun;	tun->attached = 1;	strcpy(ifr->ifr_name, tun->dev->name);	return 0; err_free_dev:	free_netdev(dev); failed:	return err;}static int tun_chr_ioctl(struct inode *inode, struct file *file,			 unsigned int cmd, unsigned long arg){	struct tun_struct *tun = file->private_data;	void __user* argp = (void __user*)arg;	struct ifreq ifr;	DECLARE_MAC_BUF(mac);	if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)		if (copy_from_user(&ifr, argp, sizeof ifr))			return -EFAULT;	if (cmd == TUNSETIFF && !tun) {		int err;		ifr.ifr_name[IFNAMSIZ-1] = '\0';		rtnl_lock();		err = tun_set_iff(file, &ifr);		rtnl_unlock();		if (err)			return err;		if (copy_to_user(argp, &ifr, sizeof(ifr)))			return -EFAULT;		return 0;	}	if (!tun)		return -EBADFD;	DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);	switch (cmd) {	case TUNSETNOCSUM:		/* Disable/Enable checksum */		if (arg)			tun->flags |= TUN_NOCHECKSUM;		else			tun->flags &= ~TUN_NOCHECKSUM;		DBG(KERN_INFO "%s: checksum %s\n",		    tun->dev->name, arg ? "disabled" : "enabled");		break;	case TUNSETPERSIST:		/* Disable/Enable persist mode */		if (arg)			tun->flags |= TUN_PERSIST;		else			tun->flags &= ~TUN_PERSIST;		DBG(KERN_INFO "%s: persist %s\n",		    tun->dev->name, arg ? "enabled" : "disabled");		break;	case TUNSETOWNER:		/* Set owner of the device */		tun->owner = (uid_t) arg;		DBG(KERN_INFO "%s: owner set to %d\n", tun->dev->name, tun->owner);		break;	case TUNSETGROUP:		/* Set group of the device */		tun->group= (gid_t) arg;		DBG(KERN_INFO "%s: group set to %d\n", tun->dev->name, tun->group);		break;	case TUNSETLINK:		/* Only allow setting the type when the interface is down */		if (tun->dev->flags & IFF_UP) {			DBG(KERN_INFO "%s: Linktype set failed because interface is up\n",				tun->dev->name);			return -EBUSY;		} else {			tun->dev->type = (int) arg;			DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type);		}		break;#ifdef TUN_DEBUG	case TUNSETDEBUG:		tun->debug = arg;		break;#endif	case SIOCGIFFLAGS:		ifr.ifr_flags = tun->if_flags;		if (copy_to_user( argp, &ifr, sizeof ifr))			return -EFAULT;		return 0;	case SIOCSIFFLAGS:		/** Set the character device's interface flags. Currently only		 * IFF_PROMISC and IFF_ALLMULTI are used. */		tun->if_flags = ifr.ifr_flags;		DBG(KERN_INFO "%s: interface flags 0x%lx\n",				tun->dev->name, tun->if_flags);		return 0;	case SIOCGIFHWADDR:		/* Note: the actual net device's address may be different */		memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr,				min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));		if (copy_to_user( argp, &ifr, sizeof ifr))			return -EFAULT;		return 0;	case SIOCSIFHWADDR:	{		/* try to set the actual net device's hw address */		int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);		if (ret == 0) {			/** Set the character device's hardware address. This is used when			 * filtering packets being sent from the network device to the character			 * device. */			memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,					min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));			DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",					tun->dev->name,					tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],					tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);		}		return  ret;	}	case SIOCADDMULTI:		/** Add the specified group to the character device's multicast filter		 * list. */		add_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);		DBG(KERN_DEBUG "%s: add multi: %s\n",		    tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));		return 0;	case SIOCDELMULTI:		/** Remove the specified group from the character device's multicast		 * filter list. */		del_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);		DBG(KERN_DEBUG "%s: del multi: %s\n",		    tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));		return 0;	default:		return -EINVAL;	};	return 0;}static int tun_chr_fasync(int fd, struct file *file, int on){	struct tun_struct *tun = file->private_data;	int ret;	if (!tun)		return -EBADFD;	DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on);	if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0)		return ret;	if (on) {		ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);		if (ret)			return ret;		tun->flags |= TUN_FASYNC;	} else		tun->flags &= ~TUN_FASYNC;	return 0;}static int tun_chr_open(struct inode *inode, struct file * file){	DBG1(KERN_INFO "tunX: tun_chr_open\n");	file->private_data = NULL;	return 0;}static int tun_chr_close(struct inode *inode, struct file *file){	struct tun_struct *tun = file->private_data;	if (!tun)		return 0;	DBG(KERN_INFO "%s: tun_chr_close\n", tun->dev->name);	tun_chr_fasync(-1, file, 0);	rtnl_lock();	/* Detach from net device */	file->private_data = NULL;	tun->attached = 0;	/* Drop read queue */	skb_queue_purge(&tun->readq);	if (!(tun->flags & TUN_PERSIST)) {		list_del(&tun->list);		unregister_netdevice(tun->dev);	}	rtnl_unlock();	return 0;}static const struct file_operations tun_fops = {	.owner	= THIS_MODULE,	.llseek = no_llseek,	.read  = do_sync_read,	.aio_read  = tun_chr_aio_read,	.write = do_sync_write,	.aio_write = tun_chr_aio_write,	.poll	= tun_chr_poll,	.ioctl	= tun_chr_ioctl,	.open	= tun_chr_open,	.release = tun_chr_close,	.fasync = tun_chr_fasync};static struct miscdevice tun_miscdev = {	.minor = TUN_MINOR,	.name = "tun",	.fops = &tun_fops,};/* ethtool interface */static int tun_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){	cmd->supported		= 0;	cmd->advertising	= 0;	cmd->speed		= SPEED_10;	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 tun_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){	struct tun_struct *tun = netdev_priv(dev);	strcpy(info->driver, DRV_NAME);	strcpy(info->version, DRV_VERSION);	strcpy(info->fw_version, "N/A");	switch (tun->flags & TUN_TYPE_MASK) {	case TUN_TUN_DEV:		strcpy(info->bus_info, "tun");		break;	case TUN_TAP_DEV:		strcpy(info->bus_info, "tap");		break;	}}static u32 tun_get_msglevel(struct net_device *dev){#ifdef TUN_DEBUG	struct tun_struct *tun = netdev_priv(dev);	return tun->debug;#else	return -EOPNOTSUPP;#endif}static void tun_set_msglevel(struct net_device *dev, u32 value){#ifdef TUN_DEBUG	struct tun_struct *tun = netdev_priv(dev);	tun->debug = value;#endif}static u32 tun_get_link(struct net_device *dev){	struct tun_struct *tun = netdev_priv(dev);	return tun->attached;}static u32 tun_get_rx_csum(struct net_device *dev){	struct tun_struct *tun = netdev_priv(dev);	return (tun->flags & TUN_NOCHECKSUM) == 0;}static int tun_set_rx_csum(struct net_device *dev, u32 data){	struct tun_struct *tun = netdev_priv(dev);	if (data)		tun->flags &= ~TUN_NOCHECKSUM;	else		tun->flags |= TUN_NOCHECKSUM;	return 0;}static const struct ethtool_ops tun_ethtool_ops = {	.get_settings	= tun_get_settings,	.get_drvinfo	= tun_get_drvinfo,	.get_msglevel	= tun_get_msglevel,	.set_msglevel	= tun_set_msglevel,	.get_link	= tun_get_link,	.get_rx_csum	= tun_get_rx_csum,	.set_rx_csum	= tun_set_rx_csum};static int __init tun_init(void){	int ret = 0;	printk(KERN_INFO "tun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);	printk(KERN_INFO "tun: %s\n", DRV_COPYRIGHT);	ret = misc_register(&tun_miscdev);	if (ret)		printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);	return ret;}static void tun_cleanup(void){	struct tun_struct *tun, *nxt;	misc_deregister(&tun_miscdev);	rtnl_lock();	list_for_each_entry_safe(tun, nxt, &tun_dev_list, list) {		DBG(KERN_INFO "%s cleaned up\n", tun->dev->name);		unregister_netdevice(tun->dev);	}	rtnl_unlock();}module_init(tun_init);module_exit(tun_cleanup);MODULE_DESCRIPTION(DRV_DESCRIPTION);MODULE_AUTHOR(DRV_COPYRIGHT);MODULE_LICENSE("GPL");MODULE_ALIAS_MISCDEV(TUN_MINOR);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?