Last active
August 1, 2024 18:08
-
-
Save abdelrahman-t/a23f57986a40f54108a71d4b91f145b2 to your computer and use it in GitHub Desktop.
Create a TUN interface in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""tun.py | |
*CAP_NET_ADMIN capability is required to create TUN interfaces.* | |
Based on: https://github.com/povilasb/iptun/blob/master/iptun/tun.py | |
""" | |
import logging as LOGGER | |
import os | |
import struct | |
import subprocess | |
import time | |
from concurrent.futures.thread import ThreadPoolExecutor | |
from fcntl import ioctl | |
from ipaddress import IPv4Address | |
LOGGER.basicConfig(level=LOGGER.DEBUG) | |
_UNIX_TUNSETIFF = 0x400454ca | |
_UNIX_IFF_TUN = 0x0001 | |
_UNIX_IFF_NO_PI = 0x1000 | |
class TUNInterface: | |
def __init__(self, name: str, address: IPv4Address): | |
self._name = name | |
self._address = address | |
# Create TUN interface. | |
self._descriptor = os.open('/dev/net/tun', os.O_RDWR) | |
ioctl(self._descriptor, | |
_UNIX_TUNSETIFF, | |
struct.pack('16sH', name.encode('ASCII'), _UNIX_IFF_TUN | _UNIX_IFF_NO_PI) | |
) | |
# Assign address to interface. | |
subprocess.call(['/sbin/ip', 'addr', 'add', str(address), 'dev', name]) | |
@property | |
def name(self) -> str: | |
return self._name | |
@property | |
def address(self) -> IPv4Address: | |
return self._address | |
def up(self) -> None: | |
# Put interface into "up" state. | |
subprocess.call(['/sbin/ip', 'link', 'set', 'dev', self._name, 'up']) | |
# Intercept outgoing packets. | |
subprocess.call(['/sbin/ip', 'route', 'add', 'default', 'dev', self._name]) | |
def read(self, number_bytes: int) -> bytes: | |
packet = os.read(self._descriptor, number_bytes) | |
LOGGER.debug('Read %d bytes from %s: %s', len(packet), self.name, packet[:10]) | |
return packet | |
def write(self, packet: bytes) -> None: | |
LOGGER.debug('Writing %s bytes to %s: %s', len(packet), self.name, packet[:10]) | |
os.write(self._descriptor, packet) | |
def test() -> None: | |
interface = TUNInterface('custom-tunnel', address=IPv4Address('10.1.0.0')) | |
interface.up() | |
def read_loop(): | |
while time.sleep(0.01) is None: | |
interface.read(1024) | |
with ThreadPoolExecutor(max_workers=2, thread_name_prefix='tun-') as pool: | |
pool.submit(read_loop) | |
if __name__ == '__main__': | |
test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment