# -*- coding: utf-8 -*- """ Acknowledgements to : - OPENWSN projet https://openwsn.atlassian.net - GlacJAY (https://gist.github.com/glacjay) https://gist.github.com/glacjay/586892 """ from . import arrow_down, arrow_up from . import messages import json import logging import os import struct import threading import time import traceback import sys if sys.platform.startswith('win32'): import _winreg as reg import win32file import win32event import pywintypes else: # linux or macos from fcntl import ioctl DEFAULT_IPV6_PREFIX = 'bbbb' logging.basicConfig(level=logging.DEBUG) log = logging.getLogger(__name__) # ============================ defines ========================================= # insert 4 octedts ID tun for compatibility (it'll be discard) VIRTUALTUNID = [0x00, 0x00, 0x86, 0xdd] IFF_TUN = 0x0001 TUNSETIFF = 0x400454ca def buf2int(buf): """ Converts some consecutive bytes of a buffer into an integer. Big-endianness is assumed. :param buf: [in] Byte array. """ returnVal = 0 for i in range(len(buf)): returnVal += buf[i] << (8 * (len(buf) - i - 1)) return returnVal # ===== formatting def formatStringBuf(buf): return '({0:>2}B) {1}'.format( len(buf), '-'.join(["%02x" % ord(b) for b in buf]), ) def formatBuf(buf): """ Format a bytelist into an easy-to-read string. For example: ``[0xab,0xcd,0xef,0x00] -> '(4B) ab-cd-ef-00'`` """ return '({0:>2}B) {1}'.format( len(buf), '-'.join(["%02x" % b for b in buf]), ) def formatIPv6Addr(addr): # group by 2 bytes addr = [buf2int(addr[2 * i:2 * i + 2]) for i in range(len(addr) / 2)] return ':'.join(["%x" % b for b in addr]) def formatAddr(addr): return '-'.join(["%02x" % b for b in addr]) def formatThreadList(): return '\nActive threads ({0})\n {1}'.format( threading.activeCount(), '\n '.join([t.name for t in threading.enumerate()]), ) # ===== parsing def hex2buf(s): """ Convert a string of hex caracters into a byte list. For example: ``'abcdef00' -> [0xab,0xcd,0xef,0x00]`` :param s: [in] The string to convert :returns: A list of integers, each element in [0x00..0xff]. """ assert type(s) == str assert len(s) % 2 == 0 returnVal = [] for i in range(len(s) / 2): realIdx = i * 2 returnVal.append(int(s[realIdx:realIdx + 2], 16)) return returnVal # ===== CRC def calculateCRC(payload): checksum = [0x00] * 2 checksum = _oneComplementSum(payload, checksum) checksum[0] ^= 0xFF checksum[1] ^= 0xFF checksum[0] = int(checksum[0]) checksum[1] = int(checksum[1]) return checksum def calculatePseudoHeaderCRC(src, dst, length, nh, payload): """ See these references: * http://www-net.cs.umass.edu/kurose/transport/UDP.html * http://tools.ietf.org/html/rfc1071 * http://en.wikipedia.org/wiki/User_Datagram_Protocol#IPv6_PSEUDO-HEADER """ checksum = [0x00] * 2 # compute pseudo header crc checksum = _oneComplementSum(src, checksum) checksum = _oneComplementSum(dst, checksum) checksum = _oneComplementSum(length, checksum) checksum = _oneComplementSum(nh, checksum) checksum = _oneComplementSum(payload, checksum) checksum[0] ^= 0xFF checksum[1] ^= 0xFF checksum[0] = int(checksum[0]) checksum[1] = int(checksum[1]) return checksum def _oneComplementSum(field, checksum): sum = 0xFFFF & (checksum[0] << 8 | checksum[1]) i = len(field) while i > 1: sum += 0xFFFF & (field[-i] << 8 | (field[-i + 1])) i -= 2 if i: sum += (0xFF & field[-1]) << 8 while sum >> 16: sum = (sum & 0xFFFF) + (sum >> 16) checksum[0] = (sum >> 8) & 0xFF checksum[1] = sum & 0xFF return checksum def byteinverse(b): # TODO: speed up through lookup table rb = 0 for pos in range(8): if b & (1 << pos) != 0: bitval = 1 else: bitval = 0 rb |= bitval << (7 - pos) return rb def calculateFCS(rpayload): payload = [] for b in rpayload: payload += [byteinverse(b)] FCS16TAB = ( 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 ) crc = 0x0000 for b in payload: crc = ((crc << 8) & 0xffff) ^ FCS16TAB[((crc >> 8) ^ b) & 0xff] returnVal = [ byteinverse(crc >> 8), byteinverse(crc & 0xff) ] return returnVal def formatCriticalMessage(error): returnVal = [] returnVal += ['Error:'] returnVal += [str(error)] returnVal += ['\ncall stack:\n'] returnVal += [traceback.format_exc()] returnVal += ['\n'] returnVal = '\n'.join(returnVal) return returnVal def formatCrashMessage(threadName, error): returnVal = [] returnVal += ['\n'] returnVal += ['======= crash in {0} ======='.format(threadName)] returnVal += [formatCriticalMessage(error)] returnVal = '\n'.join(returnVal) return returnVal class TunReadThreadPosix(threading.Thread): """ Thread which continuously reads input from a TUN interface. When data is received from the interface, it calls a callback configured during instantiation. """ ETHERNET_MTU = 1500 IPv6_HEADER_LENGTH = 40 def __init__(self, tunIf, callback): # store params self.tunIf = tunIf self.callback = callback # process event used as by parent thread self.shutdown_flag = threading.Event() # initialize parent threading.Thread.__init__(self) # give this thread a name self.name = self.__class__.__name__ # check if running on MacOs, in this situation tuntap driver doesnt put the 4extra bytes # tested with brew install Caskroom/cask/tuntap self.tunTapHeader = not sys.platform.startswith('darwin') # start myself self.start() def run(self): try: p = [] while not self.shutdown_flag.is_set(): # wait for data p = os.read(self.tunIf, self.ETHERNET_MTU) # convert input from a string to a byte list p = [ord(b) for b in p] # debug info log.debug('packet captured on tun interface: {0}'.format(formatBuf(p))) # remove tun ID octets if self.tunTapHeader: p = p[4:] # make sure it's an IPv6 packet (i.e., starts with 0x6x) if (p[0] & 0xf0) != 0x60: log.info('this is not an IPv6 packet') log.debug('first bytes: {0}'.format(formatBuf(p[:2]))) continue # because of the nature of tun for Windows, p contains ETHERNET_MTU # bytes. Cut at length of IPv6 packet. p = p[:self.IPv6_HEADER_LENGTH + 256 * p[4] + p[5]] # call the callback self.callback(p) except Exception as err: errMsg = formatCrashMessage(self.name, err) log.critical(errMsg) print("%s: bye!" % self.name) sys.exit(1) class TunReadThreadWin32(threading.Thread): ''' Thread which continuously reads input from a TUN interface. When data is received from the interface, it calls a callback configured during instantiation. ''' ETHERNET_MTU = 1500 IPv6_HEADER_LENGTH = 40 def __init__(self, tunIf, callback): # store params self.tunIf = tunIf self.callback = callback # local variables self.goOn = True self.overlappedRx = pywintypes.OVERLAPPED() self.overlappedRx.hEvent = win32event.CreateEvent(None, 0, 0, None) # initialize parent threading.Thread.__init__(self) # give this thread a name self.name = 'TunReadThread' # start myself self.start() def run(self): try: rxbuffer = win32file.AllocateReadBuffer(self.ETHERNET_MTU) while self.goOn: # wait for data try: l, p = win32file.ReadFile(self.tunIf, rxbuffer, self.overlappedRx) win32event.WaitForSingleObject(self.overlappedRx.hEvent, win32event.INFINITE) self.overlappedRx.Offset = self.overlappedRx.Offset + len(p) except Exception as err: print(err) log.error(err) raise ValueError('Error writing to TUN') else: # convert input from a string to a byte list p = [ord(b) for b in p] # print "tun input" # print p # make sure it's an IPv6 packet (starts with 0x6x) if (p[0] & 0xf0) != 0x60: # this is not an IPv6 packet continue # because of the nature of tun for Windows, p contains ETHERNET_MTU # bytes. Cut at length of IPv6 packet. p = p[:self.IPv6_HEADER_LENGTH + 256 * p[4] + p[5]] # call the callback self.callback(p) except Exception as err: errMsg = formatCrashMessage(self.name, err) log.critical(errMsg) sys.exit(1) class TunBase(object): ''' Class which interfaces between a TUN virtual interface and an EventBus. ''' def __init__(self, name, rmq_connection=None, rmq_exchange="amq.topic", ipv6_prefix=None, ipv6_host=None, ipv6_no_forwarding=None, ipv4_host=None, ipv4_network=None, ipv4_netmask=None, re_route_packets_if=None, re_route_packets_prefix=None, re_route_packets_host=None ): # RMQ setups if rmq_connection: self.connection = rmq_connection else: from kombu import Connection from urlparse import urlparse # py2 only log.warning('No connection defined, trying to import from ENVIRONMENT the AMQP_URL var') try: env_url = str(os.environ['AMQP_URL']) p = urlparse(env_url) # iterate over url's query part # keep only connection_timeout as url query, the rest of query params are not accepted by kombu query = "connection_timeout=5" for i in p.query.split('&'): if i.startswith("connection_timeout"): query = i break kombu_url = "amqp://{user}:{password}@{hostname}:{port}/{virtual_host}?{query}".format( user=p.username, password=p.password, hostname=p.hostname, port=p.port, virtual_host=p.path.strip('/'), query=query) log.info('trying to connect with URL: %s' % kombu_url) except KeyError: log.error("Please export/set the environment var AMQP_URL, then restart agent") sys.exit(1) log.warning('No connection defined, trying to create connection') self.connection = Connection(kombu_url, transport_options={'confirm_publish': True}, ) self.producer = self.connection.Producer(serializer='json') self.exchange = rmq_exchange self.name = name self.ifname = None self.packet_count = 0 if ipv6_prefix is None: # self.ipv6_prefix = [0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] ipv6_prefix = DEFAULT_IPV6_PREFIX self.ipv6_prefix = ipv6_prefix if ipv6_host is None: # self.ipv6_host = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01] ipv6_host = "1" self.ipv6_host = ipv6_host if ipv6_no_forwarding is None: ipv6_no_forwarding = False self.ipv6_no_forwarding = ipv6_no_forwarding if ipv4_host is None: ipv4_host = [10, 2, 0, 1] self.ipv4_host = ipv4_host if ipv4_network is None: ipv4_network = [10, 2, 0, 0] self.ipv4_network = ipv4_network if ipv4_netmask is None: ipv4_netmask = [255, 255, 0, 0] self.ipv4_netmask = ipv4_netmask self.re_route_packets_if = re_route_packets_if self.re_route_packets_prefix = re_route_packets_prefix self.re_route_packets_host = re_route_packets_host log.debug("IP info: \n {}".format(self.get_tun_configuration())) # create interface self.tunIf = self._create_tun_interface() # launces thread for reading tun if self.tunIf: self.tunReadThread = self._create_tun_read_thread() else: self.tunReadThread = None def close(self): logging.info("Stopping %s ..." % self.name) if self.tunReadThread: logging.info("Trying to stop %s ..." % self.tunReadThread.name) # self.tunReadThread.terminate() # logging.info("Waiting for joining %s ..." % self.tunReadThread.name) # self.tunReadThread.join(timeout=10) # time.sleep(1) logging.info("Thread stopped %s ..." % self.tunReadThread.name) sys.exit(0) # ======================== public ========================================== def get_tun_configuration(self): return { 'ipv6_prefix': self.ipv6_prefix, 'ipv6_host': self.ipv6_host, 'ipv6_no_forwarding': self.ipv6_no_forwarding, 'ipv4_host': self.ipv4_host, 'ipv4_network': self.ipv4_network, 'ipv4_netmask': self.ipv4_netmask, 're_route_packets_if': self.re_route_packets_if, 're_route_packets_prefix': self.re_route_packets_prefix, 're_route_packets_host': self.re_route_packets_host, } # ======================== private ========================================= def _tun_to_event_bus(self, data): """ Called when receiving data from the TUN interface. This function forwards the data to the the EventBus. """ routing_key = messages.MsgPacketSniffedRaw.routing_key.replace('*', self.name) log.debug("Pushing message to topic: %s" % routing_key) self.packet_count += 1 log.info("Messaged captured in tun. Pushing message to testing tool. Message count (uplink): %s" % self.packet_count) # dispatch to EventBus m = messages.MsgPacketSniffedRaw( interface_name=self.ifname, timestamp=time.time(), data=data ) print(arrow_up) log.info('\n # # # # # # # # # # # # OPEN TUN # # # # # # # # # # # # ' + '\n data packet TUN interface -> EventBus' + '\n' + m.to_json() + '\n # # # # # # # # # # # # # # # # # # # # # # # # # # # # #' ) # do not re-encode on json, producer does serialization self.producer.publish(m.to_dict(), exchange=self.exchange, routing_key=routing_key) def _get_network_prefix_notif(self, sender, signal, data): return self.ipv6_prefix def _create_tun_interface(self): ''' Open a TUN/TAP interface and switch it to TUN mode. :returns: The handler of the interface, which can be used for later read/write operations. ''' raise NotImplementedError('Child class must implement. This is OS-specific ') def _create_tun_read_thread(self): ''' Creates and starts the thread to read messages arriving from the TUN interface. ''' raise NotImplementedError('Child class must implement. This is OS-specific ') def _event_bus_to_tun(self, sender, signal, data): """ Called when receiving data from the EventBus. This function forwards the data to the the TUN interface. """ raise NotImplementedError('Child class must implement. This is OS-specific ') # def close(self): # if self.tunReadThread: # self.tunReadThread.close() # # Send a packet to openTun interface to break out of blocking read. # attempts = 0 # while self.tunReadThread.isAlive() and attempts < 3: # attempts += 1 # try: # log.info('Sending UDP packet to close openTun') # sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) # # Destination must route through the TUN host, but not be the host itself. # # OK if host does not really exist. # dst = self.ipv6_prefix + self.ipv6_host # dst[15] += 1 # # Payload and destination port are arbitrary # sock.sendto('stop', (formatIPv6Addr(dst),18004)) # # Give thread some time to exit # time.sleep(0.05) # except Exception as err: # log.error('Unable to send UDP to close tunReadThread: {0}'.join(err)) class OpenTunWindows(TunBase): ''' Class which interfaces between a TUN virtual interface and an EventBus. ''' @classmethod def get_ctl_code(cls, device_type, function, method, access): return (device_type << 16) | (access << 14) | (function << 2) | method @classmethod def get_tap_control_code(cls, request, method): return cls.get_ctl_code(34, request, method, 0) # Key in the Windows registry where to find all network interfaces (don't change, this is always the same) ADAPTER_KEY = r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}' # Value of the ComponentId key in the registry corresponding to your TUN interface. TUNTAP_COMPONENT_ID = 'tap0901' def __init__(self, *args, **kwargs): self.TAP_IOCTL_SET_MEDIA_STATUS = self.get_tap_control_code(6, 0) self.TAP_IOCTL_CONFIG_TUN = self.get_tap_control_code(10, 0) self.MIN_DEVICEIO_BUFFER_SIZE = 1 # log log.info("create instance") # Windows-specific local variables self.overlappedTx = pywintypes.OVERLAPPED() self.overlappedTx.hEvent = win32event.CreateEvent(None, 0, 0, None) # initialize parent class super(OpenTunWindows, self).__init__(*args, **kwargs) # ======================== public ========================================== # ======================== private ========================================= def _event_bus_to_tun(self, sender, signal, data): ''' Called when receiving data from the EventBus. This function forwards the data to the the TUN interface. ''' # convert data to string data = ''.join([chr(b) for b in data]) # write over tuntap interface try: win32file.WriteFile(self.tunIf, data, self.overlappedTx) win32event.WaitForSingleObject(self.overlappedTx.hEvent, win32event.INFINITE) self.overlappedTx.Offset = self.overlappedTx.Offset + len(data) if log.isEnabledFor(logging.DEBUG): log.debug("data dispatched to tun correctly {0}, {1}".format(signal, sender)) except Exception as err: errMsg = formatCriticalMessage(err) print(errMsg) log.critical(errMsg) def _create_tun_interface(self): ''' Open a TUN/TAP interface and switch it to TUN mode. :returns: The handler of the interface, which can be used for later read/write operations. ''' # retrieve the ComponentId from the TUN/TAP interface componentId = self._get_tuntap_component_id() self.ifname = '%s.tap' % componentId # create a win32file for manipulating the TUN/TAP interface tunIf = win32file.CreateFile( r'\\.\Global\%s.tap' % componentId, win32file.GENERIC_READ | win32file.GENERIC_WRITE, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None, win32file.OPEN_EXISTING, win32file.FILE_ATTRIBUTE_SYSTEM | win32file.FILE_FLAG_OVERLAPPED, None ) # have Windows consider the interface now connected win32file.DeviceIoControl( tunIf, self.TAP_IOCTL_SET_MEDIA_STATUS, '\x01\x00\x00\x00', self.MIN_DEVICEIO_BUFFER_SIZE ) # prepare the parameter passed to the TAP_IOCTL_CONFIG_TUN commmand. # This needs to be a 12-character long string representing # - the tun interface's IPv4 address (4 characters) # - the tun interface's IPv4 network address (4 characters) # - the tun interface's IPv4 network mask (4 characters) configTunParam = [] configTunParam += self.ipv4_host configTunParam += self.ipv4_network configTunParam += self.ipv4_netmask configTunParam = ''.join([chr(b) for b in configTunParam]) # switch to TUN mode (by default the interface runs in TAP mode) win32file.DeviceIoControl( tunIf, self.TAP_IOCTL_CONFIG_TUN, configTunParam, self.MIN_DEVICEIO_BUFFER_SIZE ) # return the handler of the TUN interface return tunIf def _create_tun_read_thread(self): ''' Creates and starts the thread to read messages arriving from the TUN interface ''' return TunReadThreadWin32( self.tunIf, self._tun_to_event_bus # super class method ) # ======================== helpers ========================================= def _get_tuntap_component_id(self): ''' Retrieve the instance ID of the TUN/TAP interface from the Windows registry, This function loops through all the sub-entries at the following location in the Windows registry: reg.HKEY_LOCAL_MACHINE, ADAPTER_KEY It looks for one which has the 'ComponentId' key set to TUNTAP_COMPONENT_ID, and returns the value of the 'NetCfgInstanceId' key. :returns: The 'ComponentId' associated with the TUN/TAP interface, a string of the form "{A9A413D7-4D1C-47BA-A3A9-92F091828881}". ''' with reg.OpenKey(reg.HKEY_LOCAL_MACHINE, self.ADAPTER_KEY) as adapters: try: for i in xrange(10000): key_name = reg.EnumKey(adapters, i) with reg.OpenKey(adapters, key_name) as adapter: try: component_id = reg.QueryValueEx(adapter, 'ComponentId')[0] if component_id == self.TUNTAP_COMPONENT_ID: return reg.QueryValueEx(adapter, 'NetCfgInstanceId')[0] except WindowsError as err: pass except WindowsError as err: pass class OpenTunLinux(object): """ Class which interfaces between a TUN virtual interface and an EventBus. """ def __init__(self, *args, **kwargs): # log log.info("create instance") # initialize parent class super(OpenTunLinux, self).__init__(*args, **kwargs) # ======================== public ========================================== # ======================== private ========================================= def _create_tun_interface(self): """ Open a TUN/TAP interface and switch it to TUN mode. :returns: The handler of the interface, which can be used for later read/write operations. """ try: # ===== log.info("opening tun interface") returnVal = os.open("/dev/net/tun", os.O_RDWR) ifs = ioctl(returnVal, TUNSETIFF, struct.pack("16sH", "tun%d", IFF_TUN)) self.ifname = ifs[:16].strip("\x00") # ===== log.info("configuring IPv6 address...") # ipv6_prefixStr = formatIPv6Addr(self.ipv6_prefix) # ipv6_hostStr = formatIPv6Addr(self.ipv6_host) # delete any : character in the host string (old API used to define those with that char) self.ipv6_host = self.ipv6_host.replace(":", "") v = os.system('ip tuntap add dev ' + self.ifname + ' mode tun user root') v = os.system('ip link set ' + self.ifname + ' up') v = os.system('ip -6 addr add ' + self.ipv6_prefix + '::' + self.ipv6_host + '/64 dev ' + self.ifname) v = os.system('ip -6 addr add fe80::' + self.ipv6_host + '/64 dev ' + self.ifname) # v = os.system("ip addr add " + self.ipv4_host + "/24 dev " + self.ifname) # ===== # NOTE: touch as little as possible the OS kernel variables if self.ipv6_no_forwarding: log.info("disabling IPv6 forwarding...") os.system('echo 0 > /proc/sys/net/ipv6/conf/{if_name}/forwarding'.format(if_name=self.ifname)) else: log.info("adding static route route...") # added 'metric 1' for router-compatibility constraint # (show ping packet on wireshark but don't send to mote at all) # TODO write predefined networking diagram second_optional_wsn_network_prefix = 'cccc' if 'client' in self.name else 'aaaa' static_routes = [ 'ip -6 route add ' + self.ipv6_prefix + ':1415:9200::/96 dev ' + self.ifname + ' metric 1', 'ip -6 route add ' + second_optional_wsn_network_prefix + '::/64 dev ' + self.ifname + ' metric 1' ] if self.re_route_packets_host and self.re_route_packets_if and self.re_route_packets_prefix: static_routes.append( 'ip -6 route add ' + self.re_route_packets_prefix + '::/64 dev ' + self.re_route_packets_if + ' metric 1' ) for route in static_routes: log.info("trying with:" + route) os.system(route) log.info("enabling IPv6 forwarding...") os.system('echo 1 > /proc/sys/net/ipv6/conf/{if_name}/forwarding'.format(if_name=self.ifname)) # ===== log.info('\ncreated following virtual interface:') log.info('-' * 72) os.system('ip addr show ' + self.ifname) log.info('-' * 72) log.info('\nupdate routing table:') os.system('ip -6 route show') log.info('-' * 72) # ===== except IOError as err: # happens when not root log.error('Could not created tun interface. Are you root? ({0})'.format(err)) returnVal = None return returnVal def _create_tun_read_thread(self): """ Creates and starts the thread to read messages arriving from the TUN interface. """ return TunReadThreadPosix( self.tunIf, self._tun_to_event_bus # super class method # super class ) def _event_bus_to_tun(self, sender, signal, data): """ Called when receiving data from the EventBus. This function forwards the data to the the TUN interface. """ # abort if not tun interface if not self.tunIf: return # add tun header data = VIRTUALTUNID + data # convert data to string data = ''.join([chr(b) for b in data]) try: # write over tuntap interface os.write(self.tunIf, data) if log.isEnabledFor(logging.DEBUG): log.debug("data dispatched to tun correctly, event: {0}, sender: {1}".format(signal, sender)) except Exception as err: errMsg = formatCriticalMessage(err) log.critical(errMsg) class OpenTunMACOS(TunBase): def __init__(self, *args, **kwargs): # log log.info("create instance") # initialize parent class super(OpenTunMACOS, self).__init__(*args, **kwargs) # ======================== public ========================================== # ======================== private ========================================= def _create_tun_interface(self): ''' Open a TUN/TAP interface and switch it to TUN mode. :returns: The handler of the interface, which can be used for later read/write operations. ''' # ===== # import random # TODO test concurrency problems with MacOs drivers when launching two agents in same PC # random_time = 1 + (random.randint(0, 1000) / 1000) # log.debug('waiting {rt} before starting the tun'.format(rt=random_time)) # time.sleep(random_time) log.info("opening tun interface") tun_counter = 0 while tun_counter < 16: try: import os self.ifname = 'tun{0}'.format(tun_counter) f = os.open("/dev/{0}".format(self.ifname), os.O_RDWR) break except OSError: tun_counter += 1 if tun_counter == 16: raise OSError('TUN device not found: check if it exists or if it is busy.' ' TunTap driver installed on MacOs?' ' Running as root?') else: # ===== log.info("configuring IPv6 address...") # prefixStr = u.formatIPv6Addr(openTun.IPV6PREFIX) # hostStr = u.formatIPv6Addr(openTun.IPV6HOST) # v=os.system('ifconfig {0} inet6 {1}:{2} prefixlen 64'.format(self.ifname, self.prefixStr, hostStr)) # v=os.system('ifconfig {0} inet6 fe80::{1} prefixlen 64 add'.format(self.ifname, hostStr)) # delete starting ":" self.ipv6_host = self.ipv6_host.replace(":", "") v = os.system( 'ifconfig {0} inet6 {1}::{2} prefixlen 64'.format(self.ifname, self.ipv6_prefix, self.ipv6_host)) v = os.system('ifconfig {0} inet6 fe80::{1} prefixlen 64 add'.format(self.ifname, self.ipv6_host)) # ===== # NOTE: touch as little as possible the OS kernel variables if self.ipv6_no_forwarding: pass # log.info("disabling IPv6 forwarding...") # os.system('sysctl -w net.inet6.ip6.forwarding=0') else: log.info("adding static route route...") # added 'metric 1' for router-compatibility constraint # (show ping packet on wireshark but don't send to mote at all) # TODO write predefined networking diagram second_optional_wsn_network_prefix = 'cccc' if 'client' in self.name else 'aaaa' static_routes = [ 'route add -inet6 {0}:1415:9200::/96 -interface {1}'.format(self.ipv6_prefix, self.ifname), 'route add -inet6 {0}::/64 -interface {1}'.format(second_optional_wsn_network_prefix, self.ifname) ] if self.re_route_packets_host and self.re_route_packets_if and self.re_route_packets_prefix: static_routes.append( 'route add -inet6 {0}::/64 -interface {1}'.format(self.re_route_packets_prefix, self.re_route_packets_if) ) for route in static_routes: log.info("trying with:" + route) os.system(route) # trying to set a gateway for this route # os.system('ip -6 route add ' + prefixStr + '::/64 via ' + IPv6Prefix + ':' + hostStr + '/64') log.info("enabling IPv6 forwarding...") os.system('sysctl -w net.inet6.ip6.forwarding=1') # ===== log.info('\ncreated following virtual interface:') print('-' * 72) os.system('ifconfig {0}'.format(self.ifname)) print('-' * 72) log.info('\nupdate routing table:') os.system('ip -6 route show') print('-' * 72) # ===== # =====start radvd # os.system('radvd start') return f def _create_tun_read_thread(self): ''' Creates and starts the thread to read messages arriving from the TUN interface. ''' return TunReadThreadPosix( self.tunIf, self._tun_to_event_bus # super class method # super class ) def _event_bus_to_tun(self, sender, signal, data): """ Called when receiving data from the EventBus. This function forwards the data to the the TUN interface. """ # abort if not tun interface if not self.tunIf: return # add tun header # data = VIRTUALTUNID + data # import binascii # stri = "" # for i in data: # if type(i)==int: # #stri += str(i) # stri += binascii.hexlify(str(i)) # else: # #stri += i.decode('utf-8') # stri += binascii.hexlify(i.decode('utf-8')) log.info('\n # # # # # # # # # # # # OPEN TUN # # # # # # # # # # # # ' + '\n data packet EventBus -> TUN' + '\n' + json.dumps(data) + '\n # # # # # # # # # # # # # # # # # # # # # # # # # # # # #' ) # convert data to string data = ''.join([chr(b) for b in data]) try: # write over tuntap interface os.write(self.tunIf, data) if log.isEnabledFor(logging.DEBUG): log.debug("data dispatched to tun correctly, event: {0}, sender: {1}".format(signal, sender)) except Exception as err: errMsg = formatCriticalMessage(err) log.critical(errMsg) # ======================== helpers =========================================