opentun.py 36.2 KB
Newer Older
1
2
# -*- coding: utf-8 -*-

3
4
5
6
7
8
9
10
11
12
"""
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

13
import json
14
15
16
17
18
19
20
21
import logging
import os
import struct
import threading
import time
import traceback
import sys

22
23
24
25
26
if sys.platform.startswith('win32'):
    import _winreg as reg
    import win32file
    import win32event
    import pywintypes
27
28
else:  # linux or macos
    from fcntl import ioctl
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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(
61
62
        len(buf),
        '-'.join(["%02x" % ord(b) for b in buf]),
63
64
65
66
67
68
69
70
71
    )


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(
72
73
        len(buf),
        '-'.join(["%02x" % b for b in buf]),
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    )


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(
89
90
        threading.activeCount(),
        '\n   '.join([t.name for t in threading.enumerate()]),
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
    )


# ===== 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


259
class TunReadThreadPosix(threading.Thread):
260
    """
261
    Thread which continuously reads input from a TUN interface.
262
263
264
265
266
267
268
269
270
271
272
273
274
275

    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

276
277
        # process event used as by parent thread
        self.shutdown_flag = threading.Event()
278
279
280
281
282

        # initialize parent
        threading.Thread.__init__(self)

        # give this thread a name
283
        self.name = self.__class__.__name__
284

Federico Sismondi's avatar
Federico Sismondi committed
285
286
287
288
        # 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')

289
290
291
292
293
294
295
        # start myself
        self.start()

    def run(self):
        try:
            p = []

296
            while not self.shutdown_flag.is_set():
297
298
299
300
301
302
303
304
305
306
307

                # 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
Federico Sismondi's avatar
Federico Sismondi committed
308
309
                if self.tunTapHeader:
                    p = p[4:]
310
311
312
313

                # 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')
Federico Sismondi's avatar
Federico Sismondi committed
314
                    log.debug('first bytes: {0}'.format(formatBuf(p[:2])))
315
316
317
318
319
320
321
322
323
324
325
326
                    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)
327
            print("%s: bye!" % self.name)
328
329
330
            sys.exit(1)


331
332
333
class TunReadThreadWin32(threading.Thread):
    '''
    Thread which continuously reads input from a TUN interface.
334

335
336
337
    When data is received from the interface, it calls a callback configured
    during instantiation.
    '''
338

339
340
    ETHERNET_MTU = 1500
    IPv6_HEADER_LENGTH = 40
341

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
    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):
    '''
402
    Class which interfaces between a TUN virtual interface and an EventBus.
403
    '''
404

405
    def __init__(self, name, rmq_connection=None, rmq_exchange="amq.topic",
406
                 ipv6_prefix=None, ipv6_host=None, ipv6_no_forwarding=None,
407
408
409
                 ipv4_host=None, ipv4_network=None, ipv4_netmask=None,
                 re_route_packets_if=None, re_route_packets_prefix=None, re_route_packets_host=None
                 ):
410
411

        # RMQ setups
412
413
414
415
        if rmq_connection:
            self.connection = rmq_connection
        else:
            from kombu import Connection
416
417
            from urlparse import urlparse  # py2 only

418
            log.warning('No connection defined, trying to import from ENVIRONMENT the AMQP_URL var')
419
420
            try:
                env_url = str(os.environ['AMQP_URL'])
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
                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)

438
439
                log.info('trying to connect with URL: %s' % kombu_url)

440
441
442
            except KeyError:
                log.error("Please export/set the environment var AMQP_URL, then restart agent")
                sys.exit(1)
443
444

            log.warning('No connection defined, trying to create connection')
445
            self.connection = Connection(kombu_url,
446
                                         transport_options={'confirm_publish': True}, )
447

448
        self.producer = self.connection.Producer(serializer='json')
449
        self.exchange = rmq_exchange
450
451

        self.name = name
Federico Sismondi's avatar
Federico Sismondi committed
452
        self.ifname = None
453
        self.packet_count = 0
454
455
456
457
458
459
460
461

        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]
462
            ipv6_host = "1"
463
464
        self.ipv6_host = ipv6_host

465
466
467
468
        if ipv6_no_forwarding is None:
            ipv6_no_forwarding = False
        self.ipv6_no_forwarding = ipv6_no_forwarding

469
        if ipv4_host is None:
470
            ipv4_host = [10, 2, 0, 1]
471
472
473
474
475
476
477
478
479
480
        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

481
482
483
484
        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

485
        log.debug("IP info: \n {}".format(self.get_tun_configuration()))
486

487
488
489
490
        # create interface
        self.tunIf = self._create_tun_interface()

        # launces thread for reading tun
491
        if self.tunIf:
492
            self.tunReadThread = self._create_tun_read_thread()
493
494
495
        else:
            self.tunReadThread = None

496
497
498
499
500
501
502
503
504
505
506
    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)

507
508
    # ======================== public ==========================================

509
510
511
512
513
514
515
516
517
    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,
518
519
520
            '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,
521
522
        }

523
    # ======================== private =========================================
524

525
526
527
    def _tun_to_event_bus(self, data):
        """
        Called when receiving data from the TUN interface.
528

529
530
        This function forwards the data to the the EventBus.
        """
531

532
533
        routing_key = messages.MsgPacketSniffedRaw.routing_key.replace('*', self.name)
        log.debug("Pushing message to topic: %s" % routing_key)
534

535
536
537
        self.packet_count += 1
        log.info("Messaged captured in tun. Pushing message to testing tool. Message count (uplink): %s"
                 % self.packet_count)
538

539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
        # 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)
555

556
    def _get_network_prefix_notif(self, sender, signal, data):
557
558
        return self.ipv6_prefix

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
    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):

632
633
        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)
634
        self.MIN_DEVICEIO_BUFFER_SIZE = 1
635

636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
        # 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()

Federico Sismondi's avatar
Federico Sismondi committed
682
683
        self.ifname = '%s.tap' % componentId

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
        # 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):
783
784
785
786
787
788
789
790
791
792
793
794
        """
        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))
795
            self.ifname = ifs[:16].strip("\x00")
796
797
798
799
800
801

            # =====
            log.info("configuring IPv6 address...")
            # ipv6_prefixStr = formatIPv6Addr(self.ipv6_prefix)
            # ipv6_hostStr = formatIPv6Addr(self.ipv6_host)

802
803
804
            # delete any : character in the host string (old API used to define those with that char)
            self.ipv6_host = self.ipv6_host.replace(":", "")

805
806
            v = os.system('ip tuntap add dev ' + self.ifname + ' mode tun user root')
            v = os.system('ip link set ' + self.ifname + ' up')
807
808
            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)
809

810
            # v = os.system("ip addr add " + self.ipv4_host + "/24 dev " + self.ifname)
811
812

            # =====
813
814

            # NOTE: touch as little as possible the OS kernel variables
815
816
            if self.ipv6_no_forwarding:
                log.info("disabling IPv6 forwarding...")
817
                os.system('echo 0 > /proc/sys/net/ipv6/conf/{if_name}/forwarding'.format(if_name=self.ifname))
818
            else:
819
820
821
822
823
824

                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
825
                second_optional_wsn_network_prefix = 'cccc' if 'client' in self.name else 'aaaa'
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840

                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)

841
                log.info("enabling IPv6 forwarding...")
842
                os.system('echo 1 > /proc/sys/net/ipv6/conf/{if_name}/forwarding'.format(if_name=self.ifname))
843
844
845

            # =====
            log.info('\ncreated following virtual interface:')
846
            log.info('-' * 72)
847
            os.system('ip addr show ' + self.ifname)
848
            log.info('-' * 72)
849
            log.info('\nupdate routing table:')
850
851
852
            os.system('ip -6 route show')
            log.info('-' * 72)
            # =====
853
854
855

        except IOError as err:
            # happens when not root
856
            log.error('Could not created tun interface. Are you root? ({0})'.format(err))
857
858
859
860
            returnVal = None

        return returnVal

861
    def _create_tun_read_thread(self):
862
863
864
865
        """
        Creates and starts the thread to read messages arriving from the
        TUN interface.
        """
866
        return TunReadThreadPosix(
867
            self.tunIf,
868
            self._tun_to_event_bus  # super class method  # super class
869
        )
870

871
    def _event_bus_to_tun(self, sender, signal, data):
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
        """
        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):
892
                log.debug("data dispatched to tun correctly, event: {0}, sender: {1}".format(signal, sender))
893
894
895
896
        except Exception as err:
            errMsg = formatCriticalMessage(err)
            log.critical(errMsg)

897

898
899
900
901
class OpenTunMACOS(TunBase):
    def __init__(self, *args, **kwargs):
        # log
        log.info("create instance")
902

903
904
        # initialize parent class
        super(OpenTunMACOS, self).__init__(*args, **kwargs)
905

906
    # ======================== public ==========================================
907

908
    # ======================== private =========================================
909

910
    def _create_tun_interface(self):
911
912
913
914
915
916
        '''
        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.
        '''
917
        # =====
918
919
920

        # import random
        # TODO test concurrency problems with MacOs drivers when launching two agents in same PC
921
922
923
        # random_time = 1 + (random.randint(0, 1000) / 1000)
        # log.debug('waiting {rt} before starting the tun'.format(rt=random_time))
        # time.sleep(random_time)
924

925
        log.info("opening tun interface")
926
927
        tun_counter = 0
        while tun_counter < 16:
928
929
            try:
                import os
930
931
                self.ifname = 'tun{0}'.format(tun_counter)
                f = os.open("/dev/{0}".format(self.ifname), os.O_RDWR)
932
933
                break
            except OSError:
934
                tun_counter += 1
935

936
        if tun_counter == 16:
937
938
939
            raise OSError('TUN device not found: check if it exists or if it is busy.'
                          ' TunTap driver installed on MacOs?'
                          ' Running as root?')
940
        else:
941

942
            # =====
943
944
945
946
            log.info("configuring IPv6 address...")
            # prefixStr = u.formatIPv6Addr(openTun.IPV6PREFIX)
            # hostStr   = u.formatIPv6Addr(openTun.IPV6HOST)

947
948
            # 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))
949

950
            # delete starting ":"
951
952
            self.ipv6_host = self.ipv6_host.replace(":", "")

953
954
955
            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))
956

957
            # =====
958
            # NOTE: touch as little as possible the OS kernel variables
959
            if self.ipv6_no_forwarding:
960
961
962
                pass
                # log.info("disabling IPv6 forwarding...")
                # os.system('sysctl -w net.inet6.ip6.forwarding=0')
963
            else:
964
965
966
967
968
969

                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
970
                second_optional_wsn_network_prefix = 'cccc' if 'client' in self.name else 'aaaa'
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989

                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')

990
991
                log.info("enabling IPv6 forwarding...")
                os.system('sysctl -w net.inet6.ip6.forwarding=1')
992

993
            # =====
994
            log.info('\ncreated following virtual interface:')
995
            print('-' * 72)
996
            os.system('ifconfig {0}'.format(self.ifname))
997
            print('-' * 72)
998
            log.info('\nupdate routing table:')
999
1000
1001
            os.system('ip -6 route show')
            print('-' * 72)
            # =====
1002

1003
1004
            # =====start radvd
            # os.system('radvd start')
1005
1006
1007

            return f

1008
    def _create_tun_read_thread(self):
1009
1010
1011
1012
        '''
        Creates and starts the thread to read messages arriving from the
        TUN interface.
        '''
1013
        return TunReadThreadPosix(
1014
            self.tunIf,
1015
            self._tun_to_event_bus  # super class method  # super class
1016
        )
1017

1018
    def _event_bus_to_tun(self, sender, signal, data):
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
        """
        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):
1054
                log.debug("data dispatched to tun correctly, event: {0}, sender: {1}".format(signal, sender))
1055
1056
1057
1058
1059
1060
        except Exception as err:
            errMsg = formatCriticalMessage(err)
            log.critical(errMsg)


            # ======================== helpers =========================================