#! /usr/bin/env python
#
# scapy.contrib.description = ForCES Inter-FE LFB type (IFE)
# scapy.contrib.status = loads

"""
    IFE - ForCES Inter-FE LFB type
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    :author:    Alexander Aring, aring@mojatatu.com
    :license:   GPLv2

        This module 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.

        This module is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

    :description:

        This module provides Scapy layers for the IFE protocol.

        normative references:
            - RFC 8013
              Forwarding and Control Element Separation (ForCES)
              Inter-FE Logical Functional Block (LFB)
              https://tools.ietf.org/html/rfc8013
"""

import functools

from scapy.data import ETHER_TYPES
from scapy.packet import Packet, bind_layers
from scapy.fields import FieldLenField, PacketListField, IntField, \
    MultipleTypeField, ShortField, ShortEnumField, StrField, PadField
from scapy.layers.l2 import Ether

ETH_P_IFE = 0xed3e
ETHER_TYPES['IFE'] = ETH_P_IFE

# The value to set for the skb mark.
IFE_META_SKBMARK = 0x0001
IFE_META_HASHID = 0x0002
# Value to set for priority in the skb structure.
IFE_META_PRIO = 0x0003
IFE_META_QMAP = 0x0004
# Value to set for the traffic control index in the skb structure.
IFE_META_TCINDEX = 0x0005

IFE_META_TYPES = {
    IFE_META_SKBMARK: "SKBMark",
    IFE_META_HASHID: "HashID",
    IFE_META_PRIO: "Prio",
    IFE_META_QMAP: "QMap",
    IFE_META_TCINDEX: "TCIndex"
}

IFE_TYPES_SHORT = [IFE_META_TCINDEX]
IFE_TYPES_INT = [
    IFE_META_SKBMARK,
    IFE_META_PRIO,
]


class IFETlv(Packet):
    """
    Parent Class interhit by all ForCES TLV strucutures
    """
    name = "IFETlv"

    fields_desc = [
        ShortEnumField("type", 0, IFE_META_TYPES),
        FieldLenField("length", None, length_of="value",
                      adjust=lambda pkt, x: x + 4),
        MultipleTypeField(
            [
                (PadField(ShortField("value", 0), 4, padwith=b'\x00'),
                 lambda pkt: pkt.type in IFE_TYPES_SHORT),
                (PadField(IntField("value", 0), 4, padwith=b'\x00'),
                 lambda pkt: pkt.type in IFE_TYPES_INT),
            ],
            PadField(IntField("value", 0), 4, padwith=b'\x00')
        ),
    ]

    def extract_padding(self, s):
        return "", s


class IFETlvStr(IFETlv):
    """
    A IFE TLV with variable payload
    """
    fields_desc = [
        ShortEnumField("type", 0, IFE_META_TYPES),
        FieldLenField("length", None, length_of="value",
                      adjust=lambda pkt, x: x + 4),
        StrField("value", "")
    ]


class IFE(Packet):
    """
    Main IFE Packet Class
    """
    name = "IFE"

    fields_desc = [
        FieldLenField("mdlen", None, length_of="tlvs",
                      adjust=lambda pkt, x: x + 2),
        PacketListField("tlvs", None, IFETlv),
    ]


IFESKBMark = functools.partial(IFETlv, type=IFE_META_SKBMARK)
IFEHashID = functools.partial(IFETlv, type=IFE_META_HASHID)
IFEPrio = functools.partial(IFETlv, type=IFE_META_PRIO)
IFEQMap = functools.partial(IFETlv, type=IFE_META_QMAP)
IFETCIndex = functools.partial(IFETlv, type=IFE_META_TCINDEX)

bind_layers(Ether, IFE, type=ETH_P_IFE)
