Imported Upstream version 12.1.0
[contrib/python-twisted.git] / twisted / pair / tuntap.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5 import errno, os
6 from twisted.python import log, reflect, components
7 from twisted.internet import base, fdesc, error
8 from twisted.pair import ethernet, ip
9
10 """
11 You need Eunuchs for twisted.pair.tuntap to work.
12
13 Eunuchs is a library containing the missing manly parts of
14 UNIX API for Python.
15
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
18 API) is missing.
19
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.
23
24 Current list of functions included:
25
26  - fchdir(2)
27  - recvmsg(2) and sendmsg(2), including use of cmsg(3)
28  - socketpair(2)
29  - support for TUN/TAP virtual network interfaces
30
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'.
34
35 """
36 from eunuchs.tuntap import opentuntap, TuntapPacketInfo, makePacketInfo
37
38 class TuntapPort(base.BasePort):
39     """A Port that reads and writes packets from/to a TUN/TAP-device.
40
41     TODO: Share general start/stop etc implementation details with
42     twisted.internet.udp.Port.
43     """
44     maxThroughput = 256 * 1024 # max bytes we read in one eventloop iteration
45
46     def __init__(self, interface, proto, maxPacketSize=8192, reactor=None):
47         if components.implements(proto, ethernet.IEthernetProtocol):
48             self.ethernet = 1
49         else:
50             self.ethernet = 0
51             assert components.implements(proto, ip.IIPProtocol) # XXX: fix me
52         base.BasePort.__init__(self, reactor)
53         self.interface = interface
54         self.protocol = proto
55         self.maxPacketSize = maxPacketSize
56         self.setLogStr()
57
58     def __repr__(self):
59         return "<%s on %s>" % (self.protocol.__class__, self.interface)
60
61     def startListening(self):
62         """Create and bind my socket, and begin listening on it.
63
64         This is called on unserialization, and must be called after creating a
65         server to begin listening on the specified port.
66         """
67         self._bindSocket()
68         self._connectToProtocol()
69
70     def _bindSocket(self):
71         log.msg("%s starting on %s"%(self.protocol.__class__, self.interface))
72         try:
73             fd, name = opentuntap(name=self.interface,
74                                   ethernet=self.ethernet,
75                                   packetinfo=0)
76         except OSError, e:
77             raise error.CannotListenError, (None, self.interface, e)
78         fdesc.setNonBlocking(fd)
79         self.interface = name
80         self.connected = 1
81         self.fd = fd
82
83     def fileno(self):
84         return self.fd
85
86     def _connectToProtocol(self):
87         self.protocol.makeConnection(self)
88         self.startReading()
89
90     def doRead(self):
91         """Called when my socket is ready for reading."""
92         read = 0
93         while read < self.maxThroughput:
94             try:
95                 data = os.read(self.fd, self.maxPacketSize)
96                 read += len(data)
97 #                pkt = TuntapPacketInfo(data)
98                 self.protocol.datagramReceived(data,
99                                                partial=0 # pkt.isPartial(),
100                                                )
101             except OSError, e:
102                 if e.errno in (errno.EWOULDBLOCK,):
103                     return
104                 else:
105                     raise
106             except IOError, e:
107                 if e.errno in (errno.EAGAIN, errno.EINTR):
108                     return
109                 else:
110                     raise
111             except:
112                 log.deferr()
113
114     def write(self, datagram):
115         """Write a datagram."""
116 #        header = makePacketInfo(0, 0)
117         try:
118             return os.write(self.fd, datagram)
119         except IOError, e:
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
126             else:
127                 raise
128
129     def writeSequence(self, seq):
130         self.write("".join(seq))
131
132     def loseConnection(self):
133         """Stop accepting connections on this port.
134
135         This will shut down my socket and call self.connectionLost().
136         """
137         self.stopReading()
138         if self.connected:
139             from twisted.internet import reactor
140             reactor.callLater(0, self.connectionLost)
141
142     stopListening = loseConnection
143
144     def connectionLost(self, reason=None):
145         """Cleans up my socket.
146         """
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()
153         self.connected = 0
154         os.close(self.fd)
155         del self.fd
156
157     def setLogStr(self):
158         self.logstr = reflect.qual(self.protocol.__class__) + " (TUNTAP)"
159
160     def logPrefix(self):
161         """Returns the name of my class, to prefix log entries with.
162         """
163         return self.logstr
164
165     def getHost(self):
166         """
167         Returns a tuple of ('TUNTAP', interface), indicating
168         the servers address
169         """
170         return ('TUNTAP',)+self.interface