1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
6 from twisted.python import log, reflect, components
7 from twisted.internet import base, fdesc, error
8 from twisted.pair import ethernet, ip
11 You need Eunuchs for twisted.pair.tuntap to work.
13 Eunuchs is a library containing the missing manly parts of
16 Eunuchs is a library of Python extension that complement the standard
17 libraries in parts where full support for the UNIX API (or the Linux
20 Most of the functions wrapped by Eunuchs are low-level, dirty, but
21 absolutely necessary functions for real systems programming. The aim is
22 to have the functions added to mainstream Python libraries.
24 Current list of functions included:
27 - recvmsg(2) and sendmsg(2), including use of cmsg(3)
29 - support for TUN/TAP virtual network interfaces
31 Eunuchs doesn't have a proper web home right now, but you can fetch
32 the source from http://ftp.debian.org/debian/pool/main/e/eunuch
33 -- debian users can just use 'apt-get install python-eunuchs'.
36 from eunuchs.tuntap import opentuntap, TuntapPacketInfo, makePacketInfo
38 class TuntapPort(base.BasePort):
39 """A Port that reads and writes packets from/to a TUN/TAP-device.
41 TODO: Share general start/stop etc implementation details with
42 twisted.internet.udp.Port.
44 maxThroughput = 256 * 1024 # max bytes we read in one eventloop iteration
46 def __init__(self, interface, proto, maxPacketSize=8192, reactor=None):
47 if components.implements(proto, ethernet.IEthernetProtocol):
51 assert components.implements(proto, ip.IIPProtocol) # XXX: fix me
52 base.BasePort.__init__(self, reactor)
53 self.interface = interface
55 self.maxPacketSize = maxPacketSize
59 return "<%s on %s>" % (self.protocol.__class__, self.interface)
61 def startListening(self):
62 """Create and bind my socket, and begin listening on it.
64 This is called on unserialization, and must be called after creating a
65 server to begin listening on the specified port.
68 self._connectToProtocol()
70 def _bindSocket(self):
71 log.msg("%s starting on %s"%(self.protocol.__class__, self.interface))
73 fd, name = opentuntap(name=self.interface,
74 ethernet=self.ethernet,
77 raise error.CannotListenError, (None, self.interface, e)
78 fdesc.setNonBlocking(fd)
86 def _connectToProtocol(self):
87 self.protocol.makeConnection(self)
91 """Called when my socket is ready for reading."""
93 while read < self.maxThroughput:
95 data = os.read(self.fd, self.maxPacketSize)
97 # pkt = TuntapPacketInfo(data)
98 self.protocol.datagramReceived(data,
99 partial=0 # pkt.isPartial(),
102 if e.errno in (errno.EWOULDBLOCK,):
107 if e.errno in (errno.EAGAIN, errno.EINTR):
114 def write(self, datagram):
115 """Write a datagram."""
116 # header = makePacketInfo(0, 0)
118 return os.write(self.fd, datagram)
120 if e.errno == errno.EINTR:
121 return self.write(datagram)
122 elif e.errno == errno.EMSGSIZE:
123 raise error.MessageLengthError, "message too long"
124 elif e.errno == errno.ECONNREFUSED:
125 raise error.ConnectionRefusedError
129 def writeSequence(self, seq):
130 self.write("".join(seq))
132 def loseConnection(self):
133 """Stop accepting connections on this port.
135 This will shut down my socket and call self.connectionLost().
139 from twisted.internet import reactor
140 reactor.callLater(0, self.connectionLost)
142 stopListening = loseConnection
144 def connectionLost(self, reason=None):
145 """Cleans up my socket.
147 log.msg('(Tuntap %s Closed)' % self.interface)
148 base.BasePort.connectionLost(self, reason)
149 if hasattr(self, "protocol"):
150 # we won't have attribute in ConnectedPort, in cases
151 # where there was an error in connection process
152 self.protocol.doStop()
158 self.logstr = reflect.qual(self.protocol.__class__) + " (TUNTAP)"
161 """Returns the name of my class, to prefix log entries with.
167 Returns a tuple of ('TUNTAP', interface), indicating
170 return ('TUNTAP',)+self.interface