#!/usr/local/bin/python3.10

# based on netlink.py at ....
# https://gforge.inria.fr/scm/viewvc.php/canso/trunk/tools/netlink.py?view=markup&revision=1360&root=mehani&pathrev=1360
# https://www.linuxjournal.com/article/7356?page=0,1
# http://smacked.org/docs/netlink.pdf
# RFC 3549 - https://tools.ietf.org/html/rfc3549

import os
import socket
from struct import pack
from struct import unpack
from struct import calcsize
from collections import namedtuple

from exabgp.util import concat_bytes_i

try:
    getattr(socket, 'AF_NETLINK')
except AttributeError:
    raise ImportError('This module only works on unix version with netlink support')


class NetMask(object):
    CIDR = {
        32: '255.255.255.255',
        31: '255.255.255.254',
        30: '255.255.255.252',
        29: '255.255.255.248',
        28: '255.255.255.240',
        27: '255.255.255.224',
        26: '255.255.255.192',
        25: '255.255.255.128',
        24: '255.255.255.0',
        23: '255.255.254.0',
        22: '255.255.252.0',
        21: '255.255.248.0',
        20: '255.255.240.0',
        19: '255.255.224.0',
        18: '255.255.192.0',
        17: '255.255.128.0',
        16: '255.255.0.0',
        15: '255.254.0.0',
        14: '255.252.0.0',
        13: '255.248.0.0',
        12: '255.240.0.0',
        11: '255.224.0.0',
        10: '255.192.0.0',
        9: '255.128.0.0',
        8: '255.0.0.0',
        7: '254.0.0.0',
        6: '252.0.0.0',
        5: '248.0.0.0',
        4: '240.0.0.0',
        3: '224.0.0.0',
        2: '192.0.0.0',
        1: '128.0.0.0',
        0: '0.0.0.0',
    }


class GlobalError(Exception):
    pass


class NetLinkError(GlobalError):
    pass


class Sequence(int):
    _instance = dict()

    def __new__(cls):
        cls._instance['next'] = cls._instance.get('next', 0) + 1
        return cls._instance['next']


class NetLinkRoute(object):
    _IGNORE_SEQ_FAULTS = True

    NETLINK_ROUTE = 0

    format = namedtuple('Message', 'type flags seq pid data')
    pid = os.getpid()
    netlink = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, NETLINK_ROUTE)

    class Header(object):
        # linux/netlink.h
        PACK = 'IHHII'
        LEN = calcsize(PACK)

    class Command(object):
        NLMSG_NOOP = 0x01
        NLMSG_ERROR = 0x02
        NLMSG_DONE = 0x03
        NLMSG_OVERRUN = 0x04

    class Flags(object):
        NLM_F_REQUEST = 0x01  # It is query message.
        NLM_F_MULTI = 0x02  # Multipart message, terminated by NLMSG_DONE
        NLM_F_ACK = 0x04  # Reply with ack, with zero or error code
        NLM_F_ECHO = 0x08  # Echo this query

        # Modifiers to GET query
        NLM_F_ROOT = 0x100  # specify tree root
        NLM_F_MATCH = 0x200  # return all matching
        NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
        NLM_F_ATOMIC = 0x400  # atomic GET

        # Modifiers to NEW query
        NLM_F_REPLACE = 0x100  # Override existing
        NLM_F_EXCL = 0x200  # Do not touch, if it exists
        NLM_F_CREATE = 0x400  # Create, if it does not exist
        NLM_F_APPEND = 0x800  # Add to end of list

    errors = {
        Command.NLMSG_ERROR: 'netlink error',
        Command.NLMSG_OVERRUN: 'netlink overrun',
    }

    @classmethod
    def encode(cls, dtype, seq, flags, body, attributes):
        attrs = Attributes.encode(attributes)
        length = cls.Header.LEN + len(attrs) + len(body)
        return pack(cls.Header.PACK, length, dtype, flags, seq, cls.pid) + body + attrs

    @classmethod
    def decode(cls, data):
        while data:
            length, ntype, flags, seq, pid = unpack(cls.Header.PACK, data[: cls.Header.LEN])
            if len(data) < length:
                raise NetLinkError("Buffer underrun")
            yield cls.format(ntype, flags, seq, pid, data[cls.Header.LEN : length])
            data = data[length:]

    @classmethod
    def send(cls, dtype, hflags, family=socket.AF_UNSPEC):
        sequence = Sequence()

        message = cls.encode(dtype, sequence, hflags, pack('Bxxx', family), {})

        cls.netlink.send(message)

        while True:
            data = cls.netlink.recv(640000)
            for mtype, flags, seq, pid, data in cls.decode(data):
                if seq != sequence:
                    if cls._IGNORE_SEQ_FAULTS:
                        continue
                    raise NetLinkError("netlink seq mismatch")
                if mtype == NetLinkRoute.Command.NLMSG_DONE:
                    raise StopIteration()
                elif dtype in cls.errors:
                    raise NetLinkError(cls.errors[mtype])
                else:
                    yield data

    # def change (self, dtype, family=socket.AF_UNSPEC):
    # 	for _ in self.send(dtype, self.Flags.NLM_F_REQUEST | self.Flags.NLM_F_CREATE,family):
    # 		yield _


class AttributesError(GlobalError):
    pass


class Attributes(object):
    class Header(object):
        PACK = 'HH'
        LEN = calcsize(PACK)

    class Type(object):
        IFA_UNSPEC = 0x00
        IFA_ADDRESS = 0x01
        IFA_LOCAL = 0x02
        IFA_LABEL = 0x03
        IFA_BROADCAST = 0x04
        IFA_ANYCAST = 0x05
        IFA_CACHEINFO = 0x06
        IFA_MULTICAST = 0x07

    @classmethod
    def decode(cls, data):
        while data:
            length, atype, = unpack(cls.Header.PACK, data[: cls.Header.LEN])
            if len(data) < length:
                raise AttributesError("Buffer underrun %d < %d" % (len(data), length))
            payload = data[cls.Header.LEN : length]
            yield atype, payload
            data = data[int((length + 3) / 4) * 4 :]

    @classmethod
    def encode(cls, attributes):
        def _encode(atype, payload):
            def pad(length, to=4):
                return (length + to - 1) & ~(to - 1)

            length = cls.Header.LEN + len(payload)
            raw = pack(cls.Header.PACK, length, atype) + payload
            pad = pad(length) - len(raw)
            if pad:
                raw += b'\0' * pad
            return raw

        return concat_bytes_i(_encode(k, v) for (k, v) in attributes.items())


class _Message(object):
    # to be defined by the subclasses
    format = namedtuple('Parent', 'to be subclassed')

    # to be defined by the subclasses
    class Header(object):
        PACK = ''
        LEN = 0

    def __init__(self, route):
        self.route = route

    def decode(self, data):
        extracted = list(unpack(self.Header.PACK, data[: self.Header.LEN]))
        attributes = Attributes.decode(data[self.Header.LEN :])
        extracted.append(dict(attributes))
        return self.format(*extracted)

    def extract(
        self, atype, flags=NetLinkRoute.Flags.NLM_F_REQUEST | NetLinkRoute.Flags.NLM_F_DUMP, family=socket.AF_UNSPEC
    ):
        for data in self.route.send(atype, flags, family):
            yield self.decode(data)


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Family    |   Reserved  |          Device Type              |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                     Interface Index                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                      Device Flags                             |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                      Change Mask                              |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class Link(_Message):
    class Header(object):
        PACK = 'BxHiII'
        LEN = calcsize(PACK)

    # linux/if_link.h
    format = namedtuple('Info', 'family type index flags change attributes')

    class Command(object):
        # linux/rtnetlink.h
        RTM_NEWLINK = 0x10  # Create a new network interface
        RTM_DELLINK = 0x11  # Destroy a network interface
        RTM_GETLINK = 0x12  # Retrieve information about a network interface (ifinfomsg)
        RTM_SETLINK = 0x13  # -

    class Type(object):
        class Family(object):
            AF_INET = socket.AF_INET
            AF_INET6 = socket.AF_INET6

        class Device(object):
            IFF_UP = 0x0001  # Interface is administratively up.
            IFF_BROADCAST = 0x0002  # Valid broadcast address set.
            IFF_DEBUG = 0x0004  # Internal debugging flag.
            IFF_LOOPBACK = 0x0008  # Interface is a loopback interface.
            IFF_POINTOPOINT = 0x0010  # Interface is a point-to-point link.
            IFF_NOTRAILERS = 0x0020  # Avoid use of trailers.
            IFF_RUNNING = 0x0040  # Interface is operationally up.
            IFF_NOARP = 0x0080  # No ARP protocol needed for this interface.
            IFF_PROMISC = 0x0100  # Interface is in promiscuous mode.
            IFF_ALLMULTI = 0x0200  # Receive all multicast packets.
            IFF_MASTER = 0x0400  # Master of a load balancing bundle.
            IFF_SLAVE = 0x0800  # Slave of a load balancing bundle.
            IFF_MULTICAST = 0x1000  # Supports multicast.

            IFF_PORTSEL = 0x2000  # Is able to select media type via ifmap.
            IFF_AUTOMEDIA = 0x4000  # Auto media selection active.
            IFF_DYNAMIC = 0x8000  # Interface was dynamically created.

            IFF_LOWER_UP = 0x10000  # driver signals L1 up
            IFF_DORMANT = 0x20000  # driver signals dormant
            IFF_ECHO = 0x40000  # echo sent packet

        class Attribute(object):
            IFLA_UNSPEC = 0x00
            IFLA_ADDRESS = 0x01
            IFLA_BROADCAST = 0x02
            IFLA_IFNAME = 0x03
            IFLA_MTU = 0x04
            IFLA_LINK = 0x05
            IFLA_QDISC = 0x06
            IFLA_STATS = 0x07

    def getLinks(self):
        return self.extract(Link.Command.RTM_GETLINK)


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Family    |     Length    |     Flags     |    Scope      |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                     Interface Index                         |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class Address(_Message):
    class Header(object):
        PACK = '4Bi'
        LEN = calcsize(PACK)

    format = namedtuple('Address', 'family prefixlen flags scope index attributes')

    class Command(object):
        RTM_NEWADDR = 0x14
        RTM_DELADDR = 0x15
        RTM_GETADDR = 0x16

    class Type(object):
        class Family(object):
            AF_INET = socket.AF_INET
            AF_INET6 = socket.AF_INET6

        class Flag(object):
            IFA_F_SECONDARY = 0x00  # For secondary address (alias interface)
            IFA_F_PERMANENT = 0x00  # For a permanent address set by the user.  When this is not set, it means the address was dynamically created (e.g., by stateless autoconfiguration).
            IFA_F_DEPRECATED = 0x00  # Defines deprecated (IPV4) address
            IFA_F_TENTATIVE = (
                0x00  # Defines tentative (IPV4) address (duplicate address detection is still in progress)
            )

        class Scope(object):
            RT_SCOPE_UNIVERSE = 0x00  # Global route
            RT_SCOPE_SITE = 0x00  # Interior route in the local autonomous system
            RT_SCOPE_LINK = 0x00  # Route on this link
            RT_SCOPE_HOST = 0x00  # Route on the local host
            RT_SCOPE_NOWHERE = 0x00  # Destination does not exist

        class Attribute(object):
            IFLA_UNSPEC = 0x00
            IFLA_ADDRESS = 0x01
            IFLA_BROADCAST = 0x02
            IFLA_IFNAME = 0x03
            IFLA_MTU = 0x04
            IFLA_LINK = 0x05
            IFLA_QDISC = 0x06
            IFLA_STATS = 0x07
            IFLA_COST = 0x08
            IFLA_PRIORITY = 0x09
            IFLA_MASTER = 0x0A
            IFLA_WIRELESS = 0x0B
            IFLA_PROTINFO = 0x0C
            IFLA_TXQLEN = 0x0D
            IFLA_MAP = 0x0E
            IFLA_WEIGHT = 0x0F
            IFLA_OPERSTATE = 0x10
            IFLA_LINKMODE = 0x11
            IFLA_LINKINFO = 0x12
            IFLA_NET_NS_PID = 0x13
            IFLA_IFALIAS = 0x14
            IFLA_NUM_VF = 0x15
            IFLA_VFINFO_LIST = 0x16
            IFLA_STATS64 = 0x17
            IFLA_VF_PORTS = 0x18
            IFLA_PORT_SELF = 0x19

    def getAddresses(self):
        return self.extract(Address.Command.RTM_GETADDR)


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Family    |    Reserved1  |           Reserved2           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                     Interface Index                         |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |           State             |     Flags     |     Type      |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class Neighbor(_Message):
    class Header(object):
        # linux/if_addr.h
        PACK = 'BxxxiHBB'
        LEN = calcsize(PACK)

    format = namedtuple('Neighbor', 'family index state flags type attributes')

    class Command(object):
        RTM_NEWNEIGH = 0x1C
        RTM_DELNEIGH = 0x1D
        RTM_GETNEIGH = 0x1E

    class Type(object):
        class Family(object):
            AF_INET = socket.AF_INET
            AF_INET6 = socket.AF_INET6

        class State(object):
            NUD_INCOMPLETE = 0x01  # Still attempting to resolve
            NUD_REACHABLE = 0x02  # A confirmed working cache entry
            NUD_STALE = 0x04  # an expired cache entry
            NUD_DELAY = 0x08  # Neighbor no longer reachable.  Traffic sent, waiting for confirmatio.
            NUD_PROBE = 0x10  # A cache entry that is currently being re-solicited
            NUD_FAILED = 0x20  # An invalid cache entry
            # Dummy states
            NUD_NOARP = 0x40  # A device which does not do neighbor discovery (ARP)
            NUD_PERMANENT = 0x80  # A static entry
            NUD_NONE = 0x00

        class Flag(object):
            NTF_USE = 0x01
            NTF_PROXY = 0x08  # A proxy ARP entry
            NTF_ROUTER = 0x80  # An IPv6 router

        class Attribute(object):
            # XXX : Not sure - starts at zero or one ... ??
            NDA_UNSPEC = 0x00  # Unknown type
            NDA_DST = 0x01  # A neighbour cache network. layer destination address
            NDA_LLADDR = 0x02  # A neighbor cache link layer address.
            NDA_CACHEINFO = 0x03  # Cache statistics
            NDA_PROBES = 0x04

    def getNeighbors(self):
        return self.extract(Neighbor.Command.RTM_GETNEIGH)


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Family    |  Src length   |  Dest length  |     TOS       |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |  Table ID   |   Protocol    |     Scope     |     Type      |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                          Flags                              |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class Network(_Message):
    class Header(object):
        # linux/if_addr.h
        PACK = '8BI'  # or is it 8Bi ?
        LEN = calcsize(PACK)

    format = namedtuple('Neighbor', 'family src_len dst_len tos table proto scope type flags attributes')

    class Command(object):
        RTM_NEWROUTE = 0x18
        RTM_DELROUTE = 0x19
        RTM_GETROUTE = 0x1A

    class Type(object):
        class Table(object):
            RT_TABLE_UNSPEC = 0x00  # An unspecified routing table
            RT_TABLE_DEFAULT = 0xFD  # The default table
            RT_TABLE_MAIN = 0xFE  # The main table
            RT_TABLE_LOCAL = 0xFF  # The local table

        class Protocol(object):
            RTPROT_UNSPEC = 0x00  # Identifies what/who added the route
            RTPROT_REDIRECT = 0x01  # By an ICMP redirect
            RTPROT_KERNEL = 0x02  # By the kernel
            RTPROT_BOOT = 0x03  # During bootup
            RTPROT_STATIC = 0x04  # By the administrator
            RTPROT_GATED = 0x08  # GateD
            RTPROT_RA = 0x09  # RDISC/ND router advertissements
            RTPROT_MRT = 0x0A  # Merit MRT
            RTPROT_ZEBRA = 0x0B  # ZEBRA
            RTPROT_BIRD = 0x0C  # BIRD
            RTPROT_DNROUTED = 0x0D  # DECnet routing daemon
            RTPROT_XORP = 0x0E  # XORP
            RTPROT_NTK = 0x0F  # Netsukuku
            RTPROT_DHCP = 0x10  # DHCP client
            # Self allocating ourself a kernel table :p
            # http://lxr.free-electrons.com/source/include/uapi/linux/rtnetlink.h#L237
            # YES WE CAN !
            RTPROT_EXABGP = 0x11  # Exa Networks ExaBGP

        class Scope(object):
            RT_SCOPE_UNIVERSE = 0x00  # Global route
            RT_SCOPE_SITE = 0xC8  # Interior route in the local autonomous system
            RT_SCOPE_LINK = 0xFD  # Route on this link
            RT_SCOPE_HOST = 0xFE  # Route on the local host
            RT_SCOPE_NOWHERE = 0xFF  # Destination does not exist

        class Type(object):
            RTN_UNSPEC = 0x00  # Unknown route.
            RTN_UNICAST = 0x01  # A gateway or direct route.
            RTN_LOCAL = 0x02  # A local interface route.
            RTN_BROADCAST = 0x03  # A local broadcast route (sent as a broadcast).
            RTN_ANYCAST = 0x04  # An anycast route.
            RTN_MULTICAST = 0x05  # A multicast route.
            RTN_BLACKHOLE = 0x06  # A silent packet dropping route.
            RTN_UNREACHABLE = 0x07  # An unreachable destination.  Packets dropped and host unreachable ICMPs are sent to the originator.
            RTN_PROHIBIT = 0x08  # A packet rejection route.  Packets are dropped and communication prohibited ICMPs are sent to the originator.
            RTN_THROW = 0x09  # When used with policy routing, continue routing lookup in another table.  Under normal routing, packets are dropped and net unreachable ICMPs are sent to the originator.
            RTN_NAT = 0x0A  # A network address translation rule.
            RTN_XRESOLVE = 0x0B  # Refer to an external resolver (not implemented).

        class Flag(object):
            RTM_F_NOTIFY = 0x100  # If the route changes, notify the user
            RTM_F_CLONED = 0x200  # Route is cloned from another route
            RTM_F_EQUALIZE = (
                0x400  # Allow randomization of next hop path in multi-path routing (currently not implemented)
            )
            RTM_F_PREFIX = 0x800  # Prefix Address

        class Attribute(object):
            RTA_UNSPEC = 0x00  # Ignored.
            RTA_DST = 0x01  # Protocol address for route destination address.
            RTA_SRC = 0x02  # Protocol address for route source address.
            RTA_IIF = 0x03  # Input interface index.
            RTA_OIF = 0x04  # Output interface index.
            RTA_GATEWAY = 0x05  # Protocol address for the gateway of the route
            RTA_PRIORITY = 0x06  # Priority of route.
            RTA_PREFSRC = 0x07  # Preferred source address in cases where more than one source address could be used.
            RTA_METRICS = 0x08  # Route metrics attributed to route and associated protocols (e.g., RTT, initial TCP window, etc.).
            RTA_MULTIPATH = 0x09  # Multipath route next hop's attributes.
            # RTA_PROTOINFO   = 0x0A  # Firewall based policy routing attribute.
            RTA_FLOW = 0x0B  # Route realm.
            RTA_CACHEINFO = 0x0C  # Cached route information.
            # RTA_SESSION     = 0x0D
            # RTA_MP_ALGO     = 0x0E
            RTA_TABLE = 0x0F

    def getRoutes(self):
        return self.extract(Network.Command.RTM_GETROUTE)

    # def _create (self, family):
    # 	raise RuntimeError('need to fix this flag thing')
    # 	for _ in self.extract(Network.Command.RTM_NEWROUTE,flags,family):
    # 		yield _

    def newRoute(self):
        network_flags = NetLinkRoute.Flags.NLM_F_REQUEST
        network_flags |= NetLinkRoute.Flags.NLM_F_CREATE
        network_flags |= NetLinkRoute.Flags.NLM_F_EXCL
        # network_flags |= NetLinkRoute.Flags.NLM_F_ACK

        family = socket.AF_INET

        attributes = Attributes()
        # Network.Type.Attribute.RTA_DST

        neighbor = self.format(
            family,
            0,  # src_len
            32,  # dst_len ( only /32 atm)
            0,  # tos
            Network.Type.Table.RT_TABLE_MAIN,
            Network.Type.Protocol.RTPROT_EXABGP,
            Network.Type.Scope.RT_SCOPE_UNIVERSE,
            0,  # type
            Network.Type.Flag.RTM_F_PREFIX,  # this may be wrong
            attributes,
        )

        # prefix = '\20\x7f\0\0\2'

        for _ in self.extract(Network.Command.RTM_NEWROUTE, network_flags, family):
            yield _

    def delRoute(self):
        return self.extract(Network.Command.RTM_DELROUTE)


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Family    |  Reserved1    |         Reserved2             |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                     Interface Index                         |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                      Qdisc handle                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                     Parent Qdisc                            |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                        TCM Info                             |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class TC(_Message):
    class Header(object):
        PACK = "BxxxiIII"
        LEN = calcsize(PACK)

    class Command(object):
        RTM_NEWQDISC = 36
        RTM_DELQDISC = 37
        RTM_GETQDISC = 38

    class Type(object):
        class Attribute(object):
            TCA_UNSPEC = 0x00
            TCA_KIND = 0x01
            TCA_OPTIONS = 0x02
            TCA_STATS = 0x03
            TCA_XSTATS = 0x04
            TCA_RATE = 0x05
            TCA_FCNT = 0x06
            TCA_STATS2 = 0x07


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |   Mode    |    Reserved1  |           Reserved2             |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                         Range                               |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


# 0                   1                   2                   3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       Packet ID                             |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                          Mark                               |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       timestamp_m                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       timestamp_u                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                          hook                               |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       indev_name                            |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       outdev_name                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |           hw_protocol       |        hw_type                |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |         hw_addrlen          |           Reserved            |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       hw_addr                               |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                       data_len                              |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                      Payload . . .                          |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class Firewall(_Message):
    class Header(object):
        PACK = "BxxxI"
        LEN = calcsize(PACK)

    class Packet(object):
        class Header(object):
            PACK = "IIIIIIIHHHHII"
            LEN = calcsize(PACK)
