A story about operating TUN / TAP, a virtual network driver used in VPN implementation, from Python. What I checked when I created https://github.com/kstm-su/dnsvpn.
--Mac or Linux (Windows not verified) --Python (verified with 3.5)
From Wikipedia
TAP simulates an Ethernet device and manipulates the data link layer. TUN simulates the network layer and manipulates IP packets and the like. TAP is used for bridge generation and TUN is used for routing.
Packets sent by the OS to a TUN / TAP device are sent to the user program connected to that device. You can also send packets from your program to the TUN / TAP device. In that case, the TUN / TAP device passes those packets to the OS protocol stack, so it looks as if the packets were received from the outside from the OS side.
If you want to handle from L3 or higher, use TUN, and if you want to handle from L2, use TAP.
Mac Can be installed with brew.
$ brew cask install tuntap
It is OK if there are tun [0-15]
and tap [0-15]
under / dev
.
Linux If the kernel is 2.2.x, 2.4.x or later, it should be included as standard
$ lsmod | grep tun
If nothing comes out, do modprobe
$ sudo modprobe tun
All you need is / dev / net / tun
.
Make TUN / TAP visible with ʻip link. Basically, you just open the device file with ʻopen
, but in the case of Linux, you need to set the device name and flag with ʻioctl`, which is a little troublesome.
When the program ends, the device is unregistered, so the sample program does not end in an infinite loop.
Mac
Just open / dev / tun *
and it's easy.
(/ Dev / tap *
when using TAP)
# tuntap-darwin.py
import os
import time
tun = os.open('/dev/tun0', os.O_RDWR)
while True:
time.sleep(1)
When you run it, you can see that the device file name is recognized as the network device name as it is.
$ sudo python tuntap-darwin.py &
$ ip link show tun0
tun0: flags=8850<POINTOPOINT,RUNNING,SIMPLEX,MULTICAST> mtu 1500
open (pid 2251)
Linux
Open / dev / net / tun
and use ʻioctlto set the device name such as TUN or TAP. When using TAP, set
TUN_TUN_DEV =>
TUN_TAP_DEV`.
# tuntap-linux.py
import os
import time
import fcntl
# https://github.com/kstm-su/dnsvpn/blob/master/lib/linux.py
import linux
tun = os.open('/dev/net/tun', os.O_RDWR)
ifr = linux.ifreq(name=b'hoge', flags=linux.IFF_NO_PI|linux.TUN_TUN_DEV)
fcntl.ioctl(tun, linux.TUNSETIFF, ifr)
while True:
time.sleep(1)
When you run it, you can see that it recognizes the string passed to the argument name
of ʻifreq` as the network device name.
$ sudo python tuntap-linux.py &
$ ip addr show hoge
6: hoge: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 500
link/none
Try sending and receiving data using the registered TUN / TAP device.
On both Mac and Linux, just receive with read
and send with write
.
However, if you do read
or write
as it is, you will get angry, so execute an external command to link up.
# tuntap.py
import os
import subprocess
tun = os.open('/dev/tun0', os.O_RDWR)
subprocess.check_call('sudo ifconfig tun0 192.168.100.2 192.168.100.1 netmask 255.255.255.0 up', shell=True)
while True:
data = os.read(tun, 1500)
print(data)
os.write(tun, data)
Try to ping the network device you created
$ sudo python3 tuntap.py &
$ ping 192.168.100.1
PING 192.168.100.1 (192.168.100.1): 56 data bytes
b'E\x00\x00Tj\x15\x00\x00@\x01\xc7?\xc0\xa8d\x02\xc0\xa8d\x01\x08\x00t\xa4\x7f\x0e\x00\x00XA\xcbh\x00\x07\xf5\x98\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
Request timeout for icmp_seq 0
b'E\x00\x00T~\xa2\x00\x00@\x01\xb2\xb2\xc0\xa8d\x02\xc0\xa8d\x01\x08\x00dk\x7f\x0e\x00\x01XA\xcbi\x00\x08\x05\xcf\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
Request timeout for icmp_seq 1
b'E\x00\x00T\xa3\x8e\x00\x00@\x01\x8d\xc6\xc0\xa8d\x02\xc0\xa8d\x01\x08\x00b7\x7f\x0e\x00\x02XA\xcbj\x00\x08\x08\x01\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
Request timeout for icmp_seq 2
b'E\x00\x00T2\x86\x00\x00@\x01\xfe\xce\xc0\xa8d\x02\xc0\xa8d\x01\x08\x00P\xf3\x7f\x0e\x00\x03XA\xcbk\x00\x08\x19C\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
Looking at Wireshark, two are displayed for each ping. This is because the received packet is sent as it is.
Recommended Posts