打开 TUN 界面会抛出 io.UnsupportedOperation 或 FileNotFoundError
Opening TUN interface throws either io.UnsupportedOperation or FileNotFoundError
不太熟悉 tun 接口的工作原理。我不确定我是否应该在我的本地机器上实际做一些事情(即创建一个 tun 接口,安装一个驱动程序或其他东西)来让它工作或自动处理。理想情况下,我想让它在 Mac 上工作,但 Linux 也能工作。
所以基本上这就是我所拥有的:
在 MacOS 中,当从下面的代码片段(取自 openthread 项目)调用 __init_osx()
时:
import os
import sys
import struct
import logging
import threading
import traceback
import subprocess
if sys.platform == "linux" or sys.platform == "linux2":
import fcntl
from select import select
import spinel.util as util
import spinel.config as CONFIG
IFF_TUN = 0x0001
IFF_TAP = 0x0002
IFF_NO_PI = 0x1000
IFF_TUNSETIFF = 0x400454ca
IFF_TUNSETOWNER = IFF_TUNSETIFF + 2
class TunInterface(object):
""" Utility class for creating a TUN network interface. """
def __init__(self, identifier):
self.identifier = identifier
self.ifname = "tun" + str(self.identifier)
self.tun = None
self.fd = None
platform = sys.platform
if platform == "linux" or platform == "linux2":
self.__init_linux()
elif platform == "darwin":
self.__init_osx()
else:
raise RuntimeError(
"Platform \"{}\" is not supported.".format(platform))
self.ifconfig("up")
#self.ifconfig("inet6 add fd00::1/64")
self.__start_tun_thread()
def __init_osx(self):
CONFIG.LOGGER.info("TUN: Starting osx " + self.ifname)
filename = "/dev/" + self.ifname
self.tun = os.open(filename, os.O_RDWR)
self.fd = self.tun
# trick osx to auto-assign a link local address
self.addr_add("fe80::1")
self.addr_del("fe80::1")
def __init_linux(self):
CONFIG.LOGGER.info("TUN: Starting linux " + self.ifname)
self.tun = open("/dev/net/tun", "r+b")
self.fd = self.tun.fileno()
ifr = struct.pack("16sH", self.ifname, IFF_TUN | IFF_NO_PI)
fcntl.ioctl(self.tun, IFF_TUNSETIFF, ifr) # Name interface tun#
fcntl.ioctl(self.tun, IFF_TUNSETOWNER, 1000) # Allow non-sudo access
def close(self):
""" Close this tunnel interface. """
if self.tun:
os.close(self.fd)
self.fd = None
self.tun = None
@classmethod
def command(cls, cmd):
""" Utility to make a system call. """
subprocess.check_call(cmd, shell=True)
def ifconfig(self, args):
""" Bring interface up and/or assign addresses. """
self.command('ifconfig ' + self.ifname + ' ' + args)
def __run_tun_thread(self):
while self.fd:
try:
ready_fd = select([self.fd], [], [])[0][0]
if ready_fd == self.fd:
packet = os.read(self.fd, 4000)
if CONFIG.DEBUG_TUN:
CONFIG.LOGGER.debug("\nTUN: RX (" + str(len(packet)) +
") " + util.hexify_str(packet))
self.write(packet)
except:
traceback.print_exc()
break
CONFIG.LOGGER.info("TUN: exiting")
if self.fd:
os.close(self.fd)
self.fd = None
def __start_tun_thread(self):
"""Start reader thread"""
self._reader_alive = True
self.receiver_thread = threading.Thread(target=self.__run_tun_thread)
self.receiver_thread.setDaemon(True)
self.receiver_thread.start()
它在 Mac 上抛出以下错误:
TUN: Starting osx tun1
Traceback (most recent call last):
File "spinel-cli.py", line 2484, in <module>
main()
File "spinel-cli.py", line 2475, in main
shell.cmdloop()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/cmd.py", line 217, in onecmd
return func(arg)
File "spinel-cli.py", line 2319, in do_ncptun
self.tun_if = TunInterface(self.nodeid)
File "/Users/nick/project/pyspinel/spinel/tun.py", line 55, in __init__
self.__init_osx()
File "/Users/nick/project/pyspinel/spinel/tun.py", line 68, in __init_osx
self.tun = os.open(filename, os.O_RDWR)
FileNotFoundError: [Errno 2] No such file or directory: '/dev/tun1'
并且在 Linux 中调用 __init_linux()
时,抛出以下错误:
TUN: Starting linux tun1
Traceback (most recent call last):
File "spinel-cli.py", line 2483, in <module>
main()
File "spinel-cli.py", line 2474, in main
shell.cmdloop()
File "/usr/lib/python3.6/cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python3.6/cmd.py", line 217, in onecmd
return func(arg)
File "spinel-cli.py", line 2318, in do_ncptun
self.tun_if = TunInterface(self.nodeid)
File "/home/nick/project/pyspinel/spinel/tun.py", line 53, in __init__
self.__init_linux()
File "/home/nick/project/pyspinel/spinel/tun.py", line 75, in __init_linux
self.tun = open("/dev/net/tun", "r+b")
io.UnsupportedOperation: File or stream is not seekable.
Mac环境:
- Python 3.7.4
- MacOS卡塔琳娜 10.15.7
Linux环境:
- Python 3.6.9
- Ubuntu 18.04.6 运行 在虚拟机中
此问题已在 linux 中由以下人员解决:
- 使用
os.open
方法打开界面,传入os.O_RDWR
选项
- 接口名称也需要按指定限制的字节数传入。
这是 linux 的更新初始化方法:
def __init_linux(self):
CONFIG.LOGGER.info("TUN: Starting linux " + self.ifname)
self.tun = os.open("/dev/net/tun", os.O_RDWR)
self.fd = self.tun.fileno()
ifr = struct.pack("16sH", bytes(self.ifname[:IFF_TUN_NAMELIMIT], 'utf-8'), IFF_TUN | IFF_NO_PI)
fcntl.ioctl(self.tun, IFF_TUNSETIFF, ifr) # Name interface tun#
fcntl.ioctl(self.tun, IFF_TUNSETOWNER, 1000) # Allow non-sudo access
其中 IFF_TUN_NAMELIMIT = 15
不太熟悉 tun 接口的工作原理。我不确定我是否应该在我的本地机器上实际做一些事情(即创建一个 tun 接口,安装一个驱动程序或其他东西)来让它工作或自动处理。理想情况下,我想让它在 Mac 上工作,但 Linux 也能工作。
所以基本上这就是我所拥有的:
在 MacOS 中,当从下面的代码片段(取自 openthread 项目)调用 __init_osx()
时:
import os
import sys
import struct
import logging
import threading
import traceback
import subprocess
if sys.platform == "linux" or sys.platform == "linux2":
import fcntl
from select import select
import spinel.util as util
import spinel.config as CONFIG
IFF_TUN = 0x0001
IFF_TAP = 0x0002
IFF_NO_PI = 0x1000
IFF_TUNSETIFF = 0x400454ca
IFF_TUNSETOWNER = IFF_TUNSETIFF + 2
class TunInterface(object):
""" Utility class for creating a TUN network interface. """
def __init__(self, identifier):
self.identifier = identifier
self.ifname = "tun" + str(self.identifier)
self.tun = None
self.fd = None
platform = sys.platform
if platform == "linux" or platform == "linux2":
self.__init_linux()
elif platform == "darwin":
self.__init_osx()
else:
raise RuntimeError(
"Platform \"{}\" is not supported.".format(platform))
self.ifconfig("up")
#self.ifconfig("inet6 add fd00::1/64")
self.__start_tun_thread()
def __init_osx(self):
CONFIG.LOGGER.info("TUN: Starting osx " + self.ifname)
filename = "/dev/" + self.ifname
self.tun = os.open(filename, os.O_RDWR)
self.fd = self.tun
# trick osx to auto-assign a link local address
self.addr_add("fe80::1")
self.addr_del("fe80::1")
def __init_linux(self):
CONFIG.LOGGER.info("TUN: Starting linux " + self.ifname)
self.tun = open("/dev/net/tun", "r+b")
self.fd = self.tun.fileno()
ifr = struct.pack("16sH", self.ifname, IFF_TUN | IFF_NO_PI)
fcntl.ioctl(self.tun, IFF_TUNSETIFF, ifr) # Name interface tun#
fcntl.ioctl(self.tun, IFF_TUNSETOWNER, 1000) # Allow non-sudo access
def close(self):
""" Close this tunnel interface. """
if self.tun:
os.close(self.fd)
self.fd = None
self.tun = None
@classmethod
def command(cls, cmd):
""" Utility to make a system call. """
subprocess.check_call(cmd, shell=True)
def ifconfig(self, args):
""" Bring interface up and/or assign addresses. """
self.command('ifconfig ' + self.ifname + ' ' + args)
def __run_tun_thread(self):
while self.fd:
try:
ready_fd = select([self.fd], [], [])[0][0]
if ready_fd == self.fd:
packet = os.read(self.fd, 4000)
if CONFIG.DEBUG_TUN:
CONFIG.LOGGER.debug("\nTUN: RX (" + str(len(packet)) +
") " + util.hexify_str(packet))
self.write(packet)
except:
traceback.print_exc()
break
CONFIG.LOGGER.info("TUN: exiting")
if self.fd:
os.close(self.fd)
self.fd = None
def __start_tun_thread(self):
"""Start reader thread"""
self._reader_alive = True
self.receiver_thread = threading.Thread(target=self.__run_tun_thread)
self.receiver_thread.setDaemon(True)
self.receiver_thread.start()
它在 Mac 上抛出以下错误:
TUN: Starting osx tun1
Traceback (most recent call last):
File "spinel-cli.py", line 2484, in <module>
main()
File "spinel-cli.py", line 2475, in main
shell.cmdloop()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/cmd.py", line 217, in onecmd
return func(arg)
File "spinel-cli.py", line 2319, in do_ncptun
self.tun_if = TunInterface(self.nodeid)
File "/Users/nick/project/pyspinel/spinel/tun.py", line 55, in __init__
self.__init_osx()
File "/Users/nick/project/pyspinel/spinel/tun.py", line 68, in __init_osx
self.tun = os.open(filename, os.O_RDWR)
FileNotFoundError: [Errno 2] No such file or directory: '/dev/tun1'
并且在 Linux 中调用 __init_linux()
时,抛出以下错误:
TUN: Starting linux tun1
Traceback (most recent call last):
File "spinel-cli.py", line 2483, in <module>
main()
File "spinel-cli.py", line 2474, in main
shell.cmdloop()
File "/usr/lib/python3.6/cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python3.6/cmd.py", line 217, in onecmd
return func(arg)
File "spinel-cli.py", line 2318, in do_ncptun
self.tun_if = TunInterface(self.nodeid)
File "/home/nick/project/pyspinel/spinel/tun.py", line 53, in __init__
self.__init_linux()
File "/home/nick/project/pyspinel/spinel/tun.py", line 75, in __init_linux
self.tun = open("/dev/net/tun", "r+b")
io.UnsupportedOperation: File or stream is not seekable.
Mac环境:
- Python 3.7.4
- MacOS卡塔琳娜 10.15.7
Linux环境:
- Python 3.6.9
- Ubuntu 18.04.6 运行 在虚拟机中
此问题已在 linux 中由以下人员解决:
- 使用
os.open
方法打开界面,传入os.O_RDWR
选项 - 接口名称也需要按指定限制的字节数传入。
这是 linux 的更新初始化方法:
def __init_linux(self):
CONFIG.LOGGER.info("TUN: Starting linux " + self.ifname)
self.tun = os.open("/dev/net/tun", os.O_RDWR)
self.fd = self.tun.fileno()
ifr = struct.pack("16sH", bytes(self.ifname[:IFF_TUN_NAMELIMIT], 'utf-8'), IFF_TUN | IFF_NO_PI)
fcntl.ioctl(self.tun, IFF_TUNSETIFF, ifr) # Name interface tun#
fcntl.ioctl(self.tun, IFF_TUNSETOWNER, 1000) # Allow non-sudo access
其中 IFF_TUN_NAMELIMIT = 15