wfb-ng/telemetry/tuntap.py
2019-03-22 22:42:31 +03:00

142 lines
4.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import mavlink
import fcntl
import struct
from collections import deque
from twisted.python import log, failure
from twisted.internet import reactor, defer, abstract, main, task
from twisted.internet.protocol import Protocol, connectionDone
from telemetry.conf import settings
from pyroute2 import IPRoute
from contextlib import closing
class TUNTAPTransport(abstract.FileDescriptor):
TUN = 0x0001
TAP = 0x0002
TUNSETIFF = 0x400454ca
IFF_NO_PI = 0x1000
def __init__(self, reactor, protocol, name, addr, dev='/dev/net/tun', mtu=1400, mode=TUN):
abstract.FileDescriptor.__init__(self, reactor)
self.queue = deque()
self.mtu = mtu
self.name = name
self.fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK)
try:
# We don't need packet info
mode |= self.IFF_NO_PI
fcntl.ioctl(self.fd, self.TUNSETIFF, struct.pack('16sH', name, mode))
with closing(IPRoute()) as ip:
ifidx = ip.link_lookup(ifname=name)[0]
_addr, _mask = addr.split('/')
ip.link('set', index=ifidx, state='up', mtu=self.mtu)
ip.addr('add', index=ifidx, address=_addr, prefixlen=int(_mask))
except Exception:
os.close(self.fd)
raise
# Connect protocol
self.protocol = protocol
self.protocol.makeConnection(self)
self.connected = 1
self.startReading()
def connectionLost(self, reason=connectionDone):
abstract.FileDescriptor.connectionLost(self, reason)
if self.fd is not None:
os.close(self.fd)
self.fd = None
return self.protocol.connectionLost(reason)
def loseConnection(self, _connDone=failure.Failure(main.CONNECTION_DONE)):
self.stopReading()
return self.connectionLost(_connDone)
def fileno(self):
return self.fd
def doRead(self):
self.protocol.dataReceived(os.read(self.fd, self.mtu))
def doWrite(self):
while self.queue:
packet = self.queue[0]
if os.write(self.fd, packet) <= 0:
return
self.queue.popleft()
# queue is empty
self.stopWriting()
# If I've got a producer who is supposed to supply me with data,
if self.producer is not None and ((not self.streamingProducer)
or self.producerPaused):
# tell them to supply some more.
self.producerPaused = False
self.producer.resumeProducing()
elif self.disconnecting:
# But if I was previously asked to let the connection die, do
# so.
return self._postLoseConnection()
elif self._writeDisconnecting:
# I was previously asked to half-close the connection. We
# set _writeDisconnected before calling handler, in case the
# handler calls loseConnection(), which will want to check for
# this attribute.
self._writeDisconnected = True
result = self._closeWriteConnection()
return result
def _isSendBufferFull(self):
return len(self.queue) > 1000
def write(self, data):
if isinstance(data, unicode): # no, really, I mean it
raise TypeError("Data must not be unicode")
if not self.connected or self._writeDisconnected:
return
if data:
self.queue.append(data)
self._maybePauseProducer()
self.startWriting()
class TUNTAPProtocol(Protocol):
noisy = False
keepalive_interval = 0.9
def __init__(self):
self.peer = None
# Sent keepalive packets
self.lc = task.LoopingCall(self.send_keepalive)
self.lc.start(self.keepalive_interval, now=False)
def _cleanup(self):
self.lc.stop()
# call from peer only!
def write(self, msg):
# Remove keepalive messages
if self.transport is not None and msg:
self.transport.write(msg)
def send_keepalive(self):
if self.peer is not None:
self.peer.write('')
def dataReceived(self, data):
self.lc.reset()
if self.peer is not None:
self.peer.write(data)
def send_rssi(self, rssi, rx_errors, rx_fec, flags):
pass