diff options
author | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:16:14 +0300 |
---|---|---|
committer | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:43:30 +0300 |
commit | b8cf9e88f4c5c64d9406af533d8948deb050d695 (patch) | |
tree | 218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/pair/tuntap.py | |
parent | 523f645a83a0ec97a0332dbc3863bb354c92a328 (diff) | |
download | ydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz |
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/pair/tuntap.py')
-rw-r--r-- | contrib/python/Twisted/py2/twisted/pair/tuntap.py | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/pair/tuntap.py b/contrib/python/Twisted/py2/twisted/pair/tuntap.py new file mode 100644 index 0000000000..69b3f9bfff --- /dev/null +++ b/contrib/python/Twisted/py2/twisted/pair/tuntap.py @@ -0,0 +1,433 @@ +# -*- test-case-name: twisted.pair.test.test_tuntap -*- +# Copyright (c) Twisted Matrix Laboratories. +# See LICENSE for details. + +""" +Support for Linux ethernet and IP tunnel devices. + +@see: U{https://en.wikipedia.org/wiki/TUN/TAP} +""" + +import os +import fcntl +import errno +import struct +import warnings + +from collections import namedtuple +from constantly import Flags, FlagConstant +from zope.interface import Attribute, Interface, implementer + +from twisted.python.util import FancyEqMixin, FancyStrMixin +from incremental import Version +from twisted.python.reflect import fullyQualifiedName +from twisted.python.deprecate import deprecated +from twisted.python import log +from twisted.internet import abstract, error, task, interfaces, defer +from twisted.pair import ethernet, raw + +__all__ = [ + "TunnelFlags", "TunnelAddress", "TuntapPort", + ] + + +_IFNAMSIZ = 16 +_TUNSETIFF = 0x400454ca +_TUNGETIFF = 0x800454d2 +_TUN_KO_PATH = b"/dev/net/tun" + + +class TunnelFlags(Flags): + """ + L{TunnelFlags} defines more flags which are used to configure the behavior + of a tunnel device. + + @cvar IFF_TUN: This indicates a I{tun}-type device. This type of tunnel + carries IP datagrams. This flag is mutually exclusive with C{IFF_TAP}. + + @cvar IFF_TAP: This indicates a I{tap}-type device. This type of tunnel + carries ethernet frames. This flag is mutually exclusive with C{IFF_TUN}. + + @cvar IFF_NO_PI: This indicates the I{protocol information} header will + B{not} be included in data read from the tunnel. + + @see: U{https://www.kernel.org/doc/Documentation/networking/tuntap.txt} + """ + IFF_TUN = FlagConstant(0x0001) + IFF_TAP = FlagConstant(0x0002) + + TUN_FASYNC = FlagConstant(0x0010) + TUN_NOCHECKSUM = FlagConstant(0x0020) + TUN_NO_PI = FlagConstant(0x0040) + TUN_ONE_QUEUE = FlagConstant(0x0080) + TUN_PERSIST = FlagConstant(0x0100) + TUN_VNET_HDR = FlagConstant(0x0200) + + IFF_NO_PI = FlagConstant(0x1000) + IFF_ONE_QUEUE = FlagConstant(0x2000) + IFF_VNET_HDR = FlagConstant(0x4000) + IFF_TUN_EXCL = FlagConstant(0x8000) + + + +@implementer(interfaces.IAddress) +class TunnelAddress(FancyStrMixin, FancyEqMixin, object): + """ + A L{TunnelAddress} represents the tunnel to which a L{TuntapPort} is bound. + """ + compareAttributes = ("_typeValue", "name") + showAttributes = (("type", lambda flag: flag.name), "name") + + @property + def _typeValue(self): + """ + Return the integer value of the C{type} attribute. Used to produce + correct results in the equality implementation. + """ + # Work-around for https://twistedmatrix.com/trac/ticket/6878 + return self.type.value + + + def __init__(self, type, name): + """ + @param type: Either L{TunnelFlags.IFF_TUN} or L{TunnelFlags.IFF_TAP}, + representing the type of this tunnel. + + @param name: The system name of the tunnel. + @type name: L{bytes} + """ + self.type = type + self.name = name + + + def __getitem__(self, index): + """ + Deprecated accessor for the tunnel name. Use attributes instead. + """ + warnings.warn( + "TunnelAddress.__getitem__ is deprecated since Twisted 14.0.0 " + "Use attributes instead.", category=DeprecationWarning, + stacklevel=2) + return ('TUNTAP', self.name)[index] + + + +class _TunnelDescription(namedtuple("_TunnelDescription", "fileno name")): + """ + Describe an existing tunnel. + + @ivar fileno: the file descriptor associated with the tunnel + @type fileno: L{int} + + @ivar name: the name of the tunnel + @type name: L{bytes} + """ + + + +class _IInputOutputSystem(Interface): + """ + An interface for performing some basic kinds of I/O (particularly that I/O + which might be useful for L{twisted.pair.tuntap}-using code). + """ + O_RDWR = Attribute("@see: L{os.O_RDWR}") + O_NONBLOCK = Attribute("@see: L{os.O_NONBLOCK}") + O_CLOEXEC = Attribute("@see: L{os.O_CLOEXEC}") + + def open(filename, flag, mode=0o777): + """ + @see: L{os.open} + """ + + + def ioctl(fd, opt, arg=None, mutate_flag=None): + """ + @see: L{fcntl.ioctl} + """ + + + def read(fd, limit): + """ + @see: L{os.read} + """ + + + def write(fd, data): + """ + @see: L{os.write} + """ + + + def close(fd): + """ + @see: L{os.close} + """ + + + def sendUDP(datagram, address): + """ + Send a datagram to a certain address. + + @param datagram: The payload of a UDP datagram to send. + @type datagram: L{bytes} + + @param address: The destination to which to send the datagram. + @type address: L{tuple} of (L{bytes}, L{int}) + + @return: The local address from which the datagram was sent. + @rtype: L{tuple} of (L{bytes}, L{int}) + """ + + + def receiveUDP(fileno, host, port): + """ + Return a socket which can be used to receive datagrams sent to the + given address. + + @param fileno: A file descriptor representing a tunnel device which the + datagram was either sent via or will be received via. + @type fileno: L{int} + + @param host: The IPv4 address at which the datagram will be received. + @type host: L{bytes} + + @param port: The UDP port number at which the datagram will be + received. + @type port: L{int} + + @return: A L{socket.socket} which can be used to receive the specified + datagram. + """ + + + +class _RealSystem(object): + """ + An interface to the parts of the operating system which L{TuntapPort} + relies on. This is most of an implementation of L{_IInputOutputSystem}. + """ + open = staticmethod(os.open) + read = staticmethod(os.read) + write = staticmethod(os.write) + close = staticmethod(os.close) + ioctl = staticmethod(fcntl.ioctl) + + O_RDWR = os.O_RDWR + O_NONBLOCK = os.O_NONBLOCK + # Introduced in Python 3.x + # Ubuntu 12.04, /usr/include/x86_64-linux-gnu/bits/fcntl.h + O_CLOEXEC = getattr(os, "O_CLOEXEC", 0o2000000) + + + +@implementer(interfaces.IListeningPort) +class TuntapPort(abstract.FileDescriptor): + """ + A Port that reads and writes packets from/to a TUN/TAP-device. + """ + maxThroughput = 256 * 1024 # Max bytes we read in one eventloop iteration + + def __init__(self, interface, proto, maxPacketSize=8192, reactor=None, + system=None): + if ethernet.IEthernetProtocol.providedBy(proto): + self.ethernet = 1 + self._mode = TunnelFlags.IFF_TAP + else: + self.ethernet = 0 + self._mode = TunnelFlags.IFF_TUN + assert raw.IRawPacketProtocol.providedBy(proto) + + if system is None: + system = _RealSystem() + self._system = system + + abstract.FileDescriptor.__init__(self, reactor) + self.interface = interface + self.protocol = proto + self.maxPacketSize = maxPacketSize + + logPrefix = self._getLogPrefix(self.protocol) + self.logstr = "%s (%s)" % (logPrefix, self._mode.name) + + + def __repr__(self): + args = (fullyQualifiedName(self.protocol.__class__),) + if self.connected: + args = args + ("",) + else: + args = args + ("not ",) + args = args + (self._mode.name, self.interface) + return "<%s %slistening on %s/%s>" % args + + + def startListening(self): + """ + Create and bind my socket, and begin listening on it. + + This must be called after creating a server to begin listening on the + specified tunnel. + """ + self._bindSocket() + self.protocol.makeConnection(self) + self.startReading() + + + def _openTunnel(self, name, mode): + """ + Open the named tunnel using the given mode. + + @param name: The name of the tunnel to open. + @type name: L{bytes} + + @param mode: Flags from L{TunnelFlags} with exactly one of + L{TunnelFlags.IFF_TUN} or L{TunnelFlags.IFF_TAP} set. + + @return: A L{_TunnelDescription} representing the newly opened tunnel. + """ + flags = ( + self._system.O_RDWR | self._system.O_CLOEXEC | + self._system.O_NONBLOCK) + config = struct.pack("%dsH" % (_IFNAMSIZ,), name, mode.value) + fileno = self._system.open(_TUN_KO_PATH, flags) + result = self._system.ioctl(fileno, _TUNSETIFF, config) + return _TunnelDescription(fileno, result[:_IFNAMSIZ].strip(b'\x00')) + + + def _bindSocket(self): + """ + Open the tunnel. + """ + log.msg( + format="%(protocol)s starting on %(interface)s", + protocol=self.protocol.__class__, + interface=self.interface) + try: + fileno, interface = self._openTunnel( + self.interface, self._mode | TunnelFlags.IFF_NO_PI) + except (IOError, OSError) as e: + raise error.CannotListenError(None, self.interface, e) + + self.interface = interface + self._fileno = fileno + + self.connected = 1 + + + def fileno(self): + return self._fileno + + + def doRead(self): + """ + Called when my socket is ready for reading. + """ + read = 0 + while read < self.maxThroughput: + try: + data = self._system.read(self._fileno, self.maxPacketSize) + except EnvironmentError as e: + if e.errno in (errno.EWOULDBLOCK, errno.EAGAIN, errno.EINTR): + return + else: + raise + except: + raise + read += len(data) + # TODO pkt.isPartial()? + try: + self.protocol.datagramReceived(data, partial=0) + except: + cls = fullyQualifiedName(self.protocol.__class__) + log.err( + None, + "Unhandled exception from %s.datagramReceived" % (cls,)) + + + def write(self, datagram): + """ + Write the given data as a single datagram. + + @param datagram: The data that will make up the complete datagram to be + written. + @type datagram: L{bytes} + """ + try: + return self._system.write(self._fileno, datagram) + except IOError as e: + if e.errno == errno.EINTR: + return self.write(datagram) + raise + + + def writeSequence(self, seq): + """ + Write a datagram constructed from a L{list} of L{bytes}. + + @param datagram: The data that will make up the complete datagram to be + written. + @type seq: L{list} of L{bytes} + """ + self.write(b"".join(seq)) + + + def stopListening(self): + """ + Stop accepting connections on this port. + + This will shut down my socket and call self.connectionLost(). + + @return: A L{Deferred} that fires when this port has stopped. + """ + self.stopReading() + if self.disconnecting: + return self._stoppedDeferred + elif self.connected: + self._stoppedDeferred = task.deferLater( + self.reactor, 0, self.connectionLost) + self.disconnecting = True + return self._stoppedDeferred + else: + return defer.succeed(None) + + + def loseConnection(self): + """ + Close this tunnel. Use L{TuntapPort.stopListening} instead. + """ + self.stopListening().addErrback(log.err) + + + def connectionLost(self, reason=None): + """ + Cleans up my socket. + + @param reason: Ignored. Do not use this. + """ + log.msg('(Tuntap %s Closed)' % self.interface) + abstract.FileDescriptor.connectionLost(self, reason) + self.protocol.doStop() + self.connected = 0 + self._system.close(self._fileno) + self._fileno = -1 + + + def logPrefix(self): + """ + Returns the name of my class, to prefix log entries with. + """ + return self.logstr + + + def getHost(self): + """ + Get the local address of this L{TuntapPort}. + + @return: A L{TunnelAddress} which describes the tunnel device to which + this object is bound. + @rtype: L{TunnelAddress} + """ + return TunnelAddress(self._mode, self.interface) + +TuntapPort.loseConnection = deprecated( + Version("Twisted", 14, 0, 0), + TuntapPort.stopListening)(TuntapPort.loseConnection) |