2019-03-22 13:59:13 -03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2024-04-01 20:49:01 -03:00
|
|
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
2019-03-26 12:02:27 -03:00
|
|
|
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; version 3.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
#
|
|
|
|
|
2019-03-22 13:59:13 -03:00
|
|
|
import os
|
2019-03-26 12:02:27 -03:00
|
|
|
from . import mavlink
|
2019-03-22 13:59:13 -03:00
|
|
|
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 pyroute2 import IPRoute
|
|
|
|
from contextlib import closing
|
2022-07-06 17:35:00 -03:00
|
|
|
from .conf import settings
|
2022-12-07 15:48:49 -04:00
|
|
|
from .proxy import ProxyProtocol
|
2019-03-22 13:59:13 -03:00
|
|
|
|
|
|
|
class TUNTAPTransport(abstract.FileDescriptor):
|
|
|
|
TUN = 0x0001
|
|
|
|
TAP = 0x0002
|
|
|
|
TUNSETIFF = 0x400454ca
|
|
|
|
IFF_NO_PI = 0x1000
|
|
|
|
|
2023-06-22 16:38:09 -03:00
|
|
|
def __init__(self, reactor, protocol, name, addr, dev=b'/dev/net/tun', mtu=1400, mode=TUN, default_route=False):
|
2019-03-22 13:59:13 -03:00
|
|
|
abstract.FileDescriptor.__init__(self, reactor)
|
|
|
|
self.queue = deque()
|
2022-12-07 15:48:49 -04:00
|
|
|
self.mtu = mtu - 2
|
2019-03-22 13:59:13 -03:00
|
|
|
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
|
2022-02-03 11:34:31 -04:00
|
|
|
fcntl.ioctl(self.fd, self.TUNSETIFF, struct.pack('16sH', name.encode('ascii'), mode))
|
2019-03-22 13:59:13 -03:00
|
|
|
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))
|
2023-06-22 16:38:09 -03:00
|
|
|
if default_route:
|
|
|
|
ip.route('add', dst='default', oif=ifidx, metric=10)
|
2019-03-22 13:59:13 -03:00
|
|
|
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
|
2022-12-07 15:48:49 -04:00
|
|
|
return self._closeWriteConnection()
|
2019-03-22 13:59:13 -03:00
|
|
|
|
|
|
|
def _isSendBufferFull(self):
|
|
|
|
return len(self.queue) > 1000
|
|
|
|
|
|
|
|
def write(self, data):
|
2019-03-26 12:02:27 -03:00
|
|
|
if not isinstance(data, (bytes, type(None))): # no, really, I mean it
|
|
|
|
raise TypeError("Only binary strings are supported")
|
2019-03-22 13:59:13 -03:00
|
|
|
|
|
|
|
if not self.connected or self._writeDisconnected:
|
|
|
|
return
|
|
|
|
|
|
|
|
if data:
|
|
|
|
self.queue.append(data)
|
|
|
|
self._maybePauseProducer()
|
|
|
|
self.startWriting()
|
|
|
|
|
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
class TUNTAPProtocol(Protocol, ProxyProtocol):
|
2019-03-22 13:59:13 -03:00
|
|
|
noisy = False
|
|
|
|
keepalive_interval = 0.9
|
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
def __init__(self, mtu, agg_timeout=None):
|
2023-07-12 09:02:30 -03:00
|
|
|
self.all_peers = []
|
2022-12-07 15:48:49 -04:00
|
|
|
ProxyProtocol.__init__(self,
|
|
|
|
agg_max_size=mtu,
|
|
|
|
agg_timeout=agg_timeout)
|
|
|
|
|
2019-03-22 13:59:13 -03:00
|
|
|
# Sent keepalive packets
|
|
|
|
self.lc = task.LoopingCall(self.send_keepalive)
|
|
|
|
self.lc.start(self.keepalive_interval, now=False)
|
|
|
|
|
2023-07-12 09:02:30 -03:00
|
|
|
def _send_to_all_peers(self, data):
|
|
|
|
for peer in self.all_peers:
|
|
|
|
self.peer.write(data)
|
|
|
|
|
2019-03-22 13:59:13 -03:00
|
|
|
def _cleanup(self):
|
|
|
|
self.lc.stop()
|
2022-12-07 15:48:49 -04:00
|
|
|
return ProxyProtocol._cleanup(self)
|
2019-03-22 13:59:13 -03:00
|
|
|
|
|
|
|
# call from peer only!
|
|
|
|
def write(self, msg):
|
|
|
|
# Remove keepalive messages
|
2022-12-07 15:48:49 -04:00
|
|
|
if self.transport is None or not msg:
|
|
|
|
return
|
2019-03-22 13:59:13 -03:00
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
# Unpack packets from batch
|
|
|
|
i = 0
|
|
|
|
while i < len(msg):
|
|
|
|
if len(msg) - i < 2:
|
|
|
|
log.msg('Corrupted tunneled packet header: %r' % (msg[i:],))
|
|
|
|
break
|
2019-03-22 13:59:13 -03:00
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
pkt_size = struct.unpack('!H', msg[i : i + 2])[0]
|
|
|
|
i += 2
|
2019-03-22 13:59:13 -03:00
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
if len(msg) - i < pkt_size:
|
|
|
|
log.msg('Truncated tunneled packet body: %r' % (msg[i:],))
|
|
|
|
break
|
2019-03-22 13:59:13 -03:00
|
|
|
|
2022-12-07 15:48:49 -04:00
|
|
|
self.transport.write(msg[i : i + pkt_size])
|
|
|
|
i += pkt_size
|
|
|
|
|
|
|
|
def send_keepalive(self):
|
2023-07-12 09:02:30 -03:00
|
|
|
# Send keepalive message via all antennas.
|
|
|
|
# This allow to use multiple directed antennas on the both ends
|
|
|
|
# and/or use different frequency channels on different cards.
|
|
|
|
self._send_to_all_peers(b'')
|
2022-12-07 15:48:49 -04:00
|
|
|
|
|
|
|
def dataReceived(self, data):
|
|
|
|
self.lc.reset() # reset keepalive timer
|
|
|
|
return self.messageReceived(struct.pack('!H', len(data)) + data)
|