#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# 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; either version 2 of the License, or (at your
# option) any later version. Please read the COPYING file.
#

import os
import array
import fcntl
import struct
import socket

class interface:
    """ ioctl stuff """

    IFNAMSIZ = 16               # interface name size

    # From <bits/ioctls.h>

    SIOCGIFADDR = 0x8915        # get PA address
    SIOCGIFBRDADDR  = 0x8919    # get broadcast PA address
    SIOCGIFCONF = 0x8912        # get iface list
    SIOCGIFFLAGS = 0x8913       # get flags
    SIOCGIFMTU = 0x8921         # get MTU size
    SIOCGIFNETMASK  = 0x891b    # get network PA mask
    SIOCSIFADDR = 0x8916        # set PA address
    SIOCSIFBRDADDR  = 0x891a    # set broadcast PA address
    SIOCSIFFLAGS = 0x8914       # set flags
    SIOCSIFMTU = 0x8922         # set MTU size
    SIOCSIFNETMASK  = 0x891c    # set network PA mask

    # From <net/if.h>    

    IFF_UP = 0x1           # Interface is up.
    IFF_BROADCAST = 0x2    # Broadcast address valid.
    IFF_DEBUG = 0x4        # Turn on debugging.
    IFF_LOOPBACK = 0x8     # Is a loopback net.
    IFF_POINTOPOINT = 0x10 # Interface is point-to-point link.
    IFF_NOTRAILERS = 0x20  # Avoid use of trailers.
    IFF_RUNNING = 0x40     # Resources allocated.
    IFF_NOARP = 0x80       # No address resolution protocol.
    IFF_PROMISC = 0x100    # Receive all packets.
    IFF_ALLMULTI = 0x200   # Receive all multicast packets.
    IFF_MASTER = 0x400     # Master of a load balancer.
    IFF_SLAVE = 0x800      # Slave of a load balancer.
    IFF_MULTICAST = 0x1000 # Supports multicast.
    IFF_PORTSEL = 0x2000   # Can set media type.
    IFF_AUTOMEDIA = 0x4000 # Auto media select active.


    def __init__(self):
        # create a socket to communicate with system
        self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    def _ioctl(self, func, args):
        return fcntl.ioctl(self.sockfd.fileno(), func, args)

    def _call(self, ifname, func, ip = None):

        if isinstance(ifname, str):
            ifreq = ifname.encode('utf-8')
        else:
            ifreq = ifname

        if ip is None:
            data = (ifreq + b'\0'*32)[:32]
        else:
            ifreq = (ifreq + b'\0' * self.IFNAMSIZ)[:self.IFNAMSIZ]
            data = struct.pack("16si4s10x", ifreq, socket.AF_INET, socket.inet_aton(ip))

        try:
            result = self._ioctl(func, data)
        except IOError:
            return None

        return result

    def getInterfaceList(self):
        """ Get all interface names in a list """
        # get interface list
        buffer = array.array('B', b'\0' * 1024)
        ifconf = struct.pack("iP", buffer.buffer_info()[1], buffer.buffer_info()[0])
        result = self._ioctl(self.SIOCGIFCONF, ifconf)

        # loop over interface names
        iflist = []
        size, ptr = struct.unpack("iP", result)
        for idx in range(0, size, 32):
            ifconf = buffer.tobytes()[idx:idx+32]
            name, dummy = struct.unpack("16s16s", ifconf)
            name, dummy = name.split(b'\0', 1)
            iflist.append(name.decode('utf-8'))

        return iflist

    def getAddr(self, ifname):
        """ Get the inet addr for an interface """
        result = self._call(ifname, self.SIOCGIFADDR)
        return socket.inet_ntoa(result[20:24])

    def getNetmask(self, ifname):
        """ Get the netmask for an interface """
        result = self._call(ifname, self.SIOCGIFNETMASK)
        return socket.inet_ntoa(result[20:24])

    def getBroadcast(self, ifname):
        """ Get the broadcast addr for an interface """
        result = self._call(ifname, self.SIOCGIFBRDADDR)
        return socket.inet_ntoa(result[20:24])

    def getStatus(self, ifname):
        """ Check whether interface is UP """
        result = self._call(ifname, self.SIOCGIFFLAGS)
        flags, = struct.unpack('H', result[16:18])
        return (flags & self.IFF_UP) != 0

    def getMTU(self, ifname):
        """ Get the MTU size of an interface """
        data = self._call(ifname, self.SIOCGIFMTU)
        mtu = struct.unpack("16si12x", data)[1]
        return mtu

    def setAddr(self, ifname, ip):
        """ Set the inet addr for an interface """
        result = self._call(ifname, self.SIOCSIFADDR, ip)

        if result and socket.inet_ntoa(result[20:24]) is ip:
            return True
        else:
            return None

    def setNetmask(self, ifname, ip):
        """ Set the netmask for an interface """
        result = self._call(ifname, self.SIOCSIFNETMASK, ip)

        if result and socket.inet_ntoa(result[20:24]) is ip:
            return True
        else:
            return None

    def setBroadcast(self, ifname, ip):
        """ Set the broadcast addr for an interface """
        result = self._call(ifname, self.SIOCSIFBRDADDR, ip)

        if socket.inet_ntoa(result[20:24]) is ip:
            return True
        else:
            return None

    def setStatusDown(self, ifname):
        """ Set interface status (UP/DOWN) """
        if isinstance(ifname, str):
            ifreq = ifname.encode('utf-8')
        else:
            ifreq = ifname
        ifreq = (ifreq + b'\0' * self.IFNAMSIZ)[:self.IFNAMSIZ]

        result = self._call(ifname, self.SIOCGIFFLAGS)
        flags, = struct.unpack('H', result[16:18])
        flags &= ~self.IFF_UP

        data = struct.pack("16sh", ifreq, flags)
        result = self._ioctl(self.SIOCSIFFLAGS, data)

        return result

    def setStatusUp(self, ifname):
        """ Set interface status (UP/DOWN) """
        if isinstance(ifname, str):
            ifreq = ifname.encode('utf-8')
        else:
            ifreq = ifname
        ifreq = (ifreq + b'\0' * self.IFNAMSIZ)[:self.IFNAMSIZ]

        flags = self.IFF_UP
        flags |= self.IFF_RUNNING
        flags |= self.IFF_BROADCAST
        flags |= self.IFF_MULTICAST
        flags &= ~self.IFF_NOARP
        flags &= ~self.IFF_PROMISC

        data = struct.pack("16sh", ifreq, flags)
        result = self._ioctl(self.SIOCSIFFLAGS, data)

        return result

    def setMTU(self, ifname, mtu):
        """ Set the MTU size of an interface """
        if isinstance(ifname, str):
            ifreq = ifname.encode('utf-8')
        else:
            ifreq = ifname
        ifreq = (ifreq + b'\0' * self.IFNAMSIZ)[:self.IFNAMSIZ]

        data = struct.pack("16si", ifreq, mtu)
        result = self._ioctl(self.SIOCSIFMTU, data)

        if struct.unpack("16si", result)[1] is mtu:
            return True
        else:
            return None

