16 ipbytes = map(ord,ip.decode("hex"))
17 return '.'.join(map(str,ipbytes))
23 '8863' : 'PPPoE discover',
27 def etherProto(packet, len=len):
29 if packet[12] == "\x81" and packet[13] == "\x00":
37 def formatPacket(packet, ether_mode):
39 stripped_packet = etherStrip(packet)
40 if not stripped_packet:
41 packet = packet.encode("hex")
43 return "malformed eth " + packet.encode("hex")
45 if packet[24:28] == "8100":
47 ethertype = tagtype.get(packet[32:36], 'eth')
48 return ethertype + " " + ( '-'.join( (
49 packet[0:12], # MAC dest
50 packet[12:24], # MAC src
51 packet[24:32], # VLAN tag
52 packet[32:36], # Ethertype/len
53 packet[36:], # Payload
57 ethertype = tagtype.get(packet[24:28], 'eth')
58 return ethertype + " " + ( '-'.join( (
59 packet[0:12], # MAC dest
60 packet[12:24], # MAC src
61 packet[24:28], # Ethertype/len
62 packet[28:], # Payload
65 packet = stripped_packet
66 packet = packet.encode("hex")
68 return "malformed ip " + packet
70 return "ip " + ( '-'.join( (
72 packet[1:2], #header length
73 packet[2:4], #diffserv/ECN
74 packet[4:8], #total length
76 packet[12:16], #flags/fragment offs
78 packet[18:20], #ip-proto
79 packet[20:24], #checksum
80 ipfmt(packet[24:32]), # src-ip
81 ipfmt(packet[32:40]), # dst-ip
82 packet[40:48] if (int(packet[1],16) > 5) else "", # options
83 packet[48:] if (int(packet[1],16) > 5) else packet[40:], # payload
86 def _packetReady(buf, ether_mode=False, len=len):
97 _,totallen = struct.unpack('HH',buf[0][:4])
98 totallen = socket.htons(totallen)
99 rv = len(buf[0]) >= totallen
100 if not rv and len(buf) > 1:
108 def _pullPacket(buf, ether_mode=False, len=len):
112 _,totallen = struct.unpack('HH',buf[0][:4])
113 totallen = socket.htons(totallen)
114 if len(buf[0]) < totallen:
115 rv = buf[0][:totallen]
116 buf[0] = buf[0][totallen:]
124 if buf[12:14] == '\x08\x10' and buf[16:18] == '\x08\x00':
125 # tagged ethernet frame
127 elif buf[12:14] == '\x08\x00':
128 # untagged ethernet frame
133 def etherWrap(packet):
135 "\x00"*6*2 # bogus src and dst mac
138 "\x00"*4, # bogus crc
141 def piStrip(buf, len=len):
147 def piWrap(buf, ether_mode, etherProto=etherProto):
149 proto = etherProto(buf)
153 "\x00\x00", # PI: 16 bits flags
154 proto, # 16 bits proto
158 _padmap = [ chr(padding) * padding for padding in xrange(127) ]
161 def encrypt(packet, crypter, len=len, padmap=_padmap):
163 padding = crypter.block_size - len(packet) % crypter.block_size
164 packet += padmap[padding]
167 return crypter.encrypt(packet)
169 def decrypt(packet, crypter, ord=ord):
172 packet = crypter.decrypt(packet)
175 padding = ord(packet[-1])
176 if not (0 < padding <= crypter.block_size):
178 raise RuntimeError, "Truncated packet"
179 packet = packet[:-padding]
185 fl = fcntl.fcntl(fd, fcntl.F_GETFL)
187 fcntl.fcntl(fd, fcntl.F_SETFL, fl)
190 traceback.print_exc(file=sys.stderr)
194 def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr=sys.stderr, reconnect=None, rwrite=None, rread=None, tunqueue=1000, tunkqueue=1000,
195 cipher='AES', accept_local=None, accept_remote=None, slowlocal=True, queueclass=None, bwlimit=None,
196 len=len, max=max, min=min, OSError=OSError, select=select.select, selecterror=select.error, os=os, socket=socket,
197 retrycodes=(os.errno.EWOULDBLOCK, os.errno.EAGAIN, os.errno.EINTR) ):
201 if cipher_key and cipher:
204 __import__('Crypto.Cipher.'+cipher)
207 cipher = getattr(Crypto.Cipher, cipher)
208 hashed_key = hashlib.sha256(cipher_key).digest()
209 if getattr(cipher, 'key_size'):
210 hashed_key = hashed_key[:cipher.key_size]
211 elif ciphername == 'DES3':
212 hashed_key = hashed_key[:24]
213 crypter = cipher.new(
218 traceback.print_exc(file=sys.stderr)
222 if stderr is not None:
224 print >>stderr, "Packets are transmitted in CIPHER"
226 print >>stderr, "Packets are transmitted in PLAINTEXT"
228 if hasattr(remote, 'fileno'):
229 remote_fd = remote.fileno()
231 def rwrite(remote, packet, os_write=os.write):
232 return os_write(remote_fd, packet)
234 def rread(remote, maxlen, os_read=os.read):
235 return os_read(remote_fd, maxlen)
237 rnonblock = nonblock(remote)
238 tnonblock = nonblock(tun)
240 # Pick up TUN/TAP writing method
245 # We have iovec, so we can skip PI injection
246 # and use iovec which does it natively
248 twrite = iovec.ethpiwrite
249 tread = iovec.piread2
251 twrite = iovec.ippiwrite
252 tread = iovec.piread2
254 # We have to inject PI headers pythonically
255 def twrite(fd, packet, oswrite=os.write, piWrap=piWrap, ether_mode=ether_mode):
256 return oswrite(fd, piWrap(packet, ether_mode))
258 # For reading, we strip PI headers with buffer slicing and that's it
259 def tread(fd, maxlen, osread=os.read, piStrip=piStrip):
260 return piStrip(osread(fd, maxlen))
262 # No need to inject PI headers
270 if accept_local is not None:
271 def tread(fd, maxlen, _tread=tread, accept=accept_local):
272 packet = _tread(fd, maxlen)
273 if accept(packet, 0):
278 if accept_remote is not None:
280 def decrypt_(packet, crypter, decrypt_=decrypt_, accept=accept_remote):
281 packet = decrypt_(packet, crypter)
282 if accept(packet, 1):
287 def rread(fd, maxlen, _rread=rread, accept=accept_remote):
288 packet = _rread(fd, maxlen)
289 if accept(packet, 1):
294 maxbkbuf = maxfwbuf = max(10,tunqueue-tunkqueue)
295 tunhurry = max(0,maxbkbuf/2)
297 if queueclass is None:
298 queueclass = collections.deque
302 maxfwbuf = maxbkbuf = 2000000000
311 if ether_mode or udp:
313 pullPacket = queueclass.popleft
314 reschedule = queueclass.appendleft
316 packetReady = _packetReady
317 pullPacket = _pullPacket
318 reschedule = queueclass.appendleft
324 maxbwfree = bwfree = 1500 * tunqueue
331 if packetReady(bkbuf):
333 if remoteok and packetReady(fwbuf) and (not bwlimit or bwfree > 0):
337 if len(fwbuf) < maxfwbuf:
339 if remoteok and len(bkbuf) < maxbkbuf:
348 rdrdy, wrdy, errs = select(rset,wset,eset,1)
349 except selecterror, e:
350 if e.args[0] == errno.EINTR:
356 if reconnect is not None and remote in errs and tun not in errs:
358 if hasattr(remote, 'fileno'):
359 remote_fd = remote.fileno()
360 elif udp and remote in errs and tun not in errs:
361 # In UDP mode, those are always transient errors
362 # Usually, an error will imply a read-ready socket
363 # that will raise an "Connection refused" error, so
364 # disable read-readiness just for now, and retry
373 # check to see if we can write
374 #rr = wr = rt = wt = 0
379 for x in xrange(maxbatch):
380 packet = pullPacket(fwbuf)
383 packet = encrypt_(packet, crypter)
385 sent += rwrite(remote, packet)
388 if not rnonblock or not packetReady(fwbuf):
391 # This except handles the entire While block on PURPOSE
392 # as an optimization (setting a try/except block is expensive)
393 # The only operation that can raise this exception is rwrite
394 if e.errno in retrycodes:
396 reschedule(fwbuf, packet)
400 if reconnect is not None:
401 # in UDP mode, sometimes connected sockets can return a connection refused.
402 # Give the caller a chance to reconnect
404 if hasattr(remote, 'fileno'):
405 remote_fd = remote.fileno()
407 # in UDP mode, we ignore errors - packet loss man...
409 #traceback.print_exc(file=sys.stderr)
415 for x in xrange(maxtbatch):
416 packet = pullPacket(bkbuf)
417 twrite(tunfd, packet)
420 # Do not inject packets into the TUN faster than they arrive, unless we're falling
421 # behind. TUN devices discard packets if their queue is full (tunkqueue), but they
422 # don't block either (they're always ready to write), so if we flood the device
423 # we'll have high packet loss.
424 if not tnonblock or (slowlocal and len(bkbuf) < tunhurry) or not packetReady(bkbuf):
428 # Give some time for the kernel to process the packets
431 # This except handles the entire While block on PURPOSE
432 # as an optimization (setting a try/except block is expensive)
433 # The only operation that can raise this exception is os_write
434 if e.errno in retrycodes:
436 reschedule(bkbuf, packet)
440 # check incoming data packets
443 for x in xrange(maxbatch):
444 packet = tread(tunfd,2000) # tun.read blocks until it gets 2k!
450 if not tnonblock or len(fwbuf) >= maxfwbuf:
453 # This except handles the entire While block on PURPOSE
454 # as an optimization (setting a try/except block is expensive)
455 # The only operation that can raise this exception is os_read
456 if e.errno not in retrycodes:
461 for x in xrange(maxbatch):
462 packet = rread(remote,2000)
466 packet = decrypt_(packet, crypter)
470 if not udp and packet == "":
471 # Connection broken, try to reconnect (or just die)
472 raise RuntimeError, "Connection broken"
478 if not rnonblock or len(bkbuf) >= maxbkbuf:
481 # This except handles the entire While block on PURPOSE
482 # as an optimization (setting a try/except block is expensive)
483 # The only operation that can raise this exception is rread
484 if e.errno not in retrycodes:
487 if reconnect is not None:
488 # in UDP mode, sometimes connected sockets can return a connection refused
489 # on read. Give the caller a chance to reconnect
491 if hasattr(remote, 'fileno'):
492 remote_fd = remote.fileno()
494 # in UDP mode, we ignore errors - packet loss man...
496 traceback.print_exc(file=sys.stderr)
500 delta = tnow - lastbwtime
502 delta = int(bwlimit * delta)
504 bwfree = min(bwfree+delta, maxbwfree)
507 #print >>sys.stderr, "rr:%d\twr:%d\trt:%d\twt:%d" % (rr,wr,rt,wt)
510 def udp_handshake(TERMINATE, rsock):
513 while not endme and not TERMINATE:
523 keepalive_thread = threading.Thread(target=keepalive)
524 keepalive_thread.start()
528 raise OSError, "Killed"
530 heartbeat = rsock.recv(10)
533 time.sleep(min(30.0,retrydelay))
536 heartbeat = rsock.recv(10)
538 keepalive_thread.join()