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 + -
显示快捷键?