#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 27 13:53:35 2018

@author: msobral
"""
from ipaddress import ip_address as IP
import struct
import fcntl
import os
import socket

class Tun:
    '''Interfaces tun: possibilita criar e destruir interfaces tun,
além de enviar e receber quadros através desse tipo de interface'''

    Defargs = {'mask': '255.255.255.255', 'mtu': 248, 'qlen': 4}
    
    ifreq = '16s24c'
    IFF_TUN = 1
    IFF_UP = 1
    IFF_RUNNING = 0x40
    PROTO_IPV4=0x8000
    PROTO_IPV6=0x866d

    TUNSETIFF = 0x400454ca
    SIOCSIFADDR = 0x8916
    SIOCSIFDSTADDR = 0x8918
    SIOCSIFNETMASK = 0x891c
    SIOCSIFMTU = 0x8922
    SIOCSIFTXQLEN = 0x8943
    SIOCGIFFLAGS = 0x8913
    SIOCSIFFLAGS = 0x8914
    
    def __init__(self, name, ip, dstip, ** args):
        '''Define uma interface tun, mas ainda não a cria. Os parâmetros de configuraçao dessa interface devem ser informados nos argumentos:
        name: nome da interface tun
        ip: endereço IPv4 desta interface
        dstip: endereço IPv4 da outra ponta do enlace
        args: "key arguments" opcionais 
          mask=máscara de rede (str)
          mtu=MTU (int)
          qlen=comprimento da fila de saída (int)
        '''
        self.name = name.encode('ascii')
        self.ip = IP(ip)
        self.dstip = IP(dstip)
        self.mask = IP(self._getarg('mask', args))
        self.mtu = self._getarg('mtu', args)
        self.qlen = self._getarg('qlen', args)
        self.fd = -1
        
    def __del__(self):
        self.stop()
        
    def _getarg(self, k, args):
        try:
            return args[k]
        except KeyError:
            return Tun.Defargs[k]
        
    def stop(self):
        'Para e remove a interface tun'
        if self.fd >= 0: os.close(self.fd)

    def start(self):
        'Cria a interface tun, e configura-a com seu endereço IP'
        if self.fd >= 0: raise ValueError('already started')
        self._alloc()
        self._setIp()
        
    def send_frame(self, dados, proto):
        '''Envia os dados para a interface tun.
           dados: buffer com os bytes a enviar (bytes ou bytearray)
           proto: número do protocolo'''
        frame = struct.pack('!HH%ds' % len(dados), 0, proto, dados)
        os.write(self.fd, frame)

    def get_frame(self):
       '''Recebe dados da tun. Retorna uma tupla (proto,dados):
           dados: buffer com os bytes recebido (tipo bytes)
           proto: número do protocolo'''
       dados = os.read(self.fd, self.mtu+4)
       flags,proto,payload = struct.unpack('!HH%ds' % (len(dados)-4), dados)
       return proto,payload

    def _alloc(self):
        ifr = bytearray(b'0'*40)
        
        self.fd = os.open('/dev/net/tun', os.O_RDWR)
        ifr = struct.pack('16sh22x', self.name, Tun.IFF_TUN)
        try:
            fcntl.ioctl(self.fd, Tun.TUNSETIFF, ifr)
        except OSError as e:
            os.close(self.fd)
            raise e
            
        if not self.name:
            name,flag = struct.unpack('16sh22x', ifr)
            self.name = name.strip(b'\x00')
            
    def _genaddr(self, ip):
        # struct sockaddr_in
        addr = struct.pack('HH4s8x', socket.AF_INET, 0, ip.packed)
        ifr = struct.pack('16s16s8x', self.name, addr)
        return ifr
        
    def _setIp(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
        fcntl.ioctl(s, Tun.SIOCSIFADDR, self._genaddr(self.ip))
        fcntl.ioctl(s, Tun.SIOCSIFDSTADDR, self._genaddr(self.dstip))
        fcntl.ioctl(s, Tun.SIOCSIFNETMASK, self._genaddr(self.mask))
        ifr = struct.pack('16sI20x', self.name, self.mtu)
        fcntl.ioctl(s, Tun.SIOCSIFMTU, ifr)
        ifr = struct.pack('16sI20x', self.name, self.qlen)
        fcntl.ioctl(s, Tun.SIOCSIFTXQLEN, ifr)
        fcntl.ioctl(s, Tun.SIOCGIFFLAGS, ifr)
        name,flag = struct.unpack('16sH22x', ifr)
        flag |= Tun.IFF_UP | Tun.IFF_RUNNING
        ifr = struct.pack('16sH22x', self.name, flag)        
        fcntl.ioctl(s, Tun.SIOCSIFFLAGS, ifr)
        
        
        
        
        
        
