/*
 * Copyright (c) 2015, EURECOM (www.eurecom.fr)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the FreeBSD Project.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


#include "TLVEncoder.h"
#include "TLVDecoder.h"
#include "TrafficFlowTemplate.h"

static int decode_traffic_flow_template_delete_packet(DeletePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int decode_traffic_flow_template_create_tft(CreateNewTft* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int decode_traffic_flow_template_add_packet(AddPacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int decode_traffic_flow_template_replace_packet(ReplacePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);

static int decode_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int decode_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);

static int encode_traffic_flow_template_delete_packet(DeletePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int encode_traffic_flow_template_create_tft(CreateNewTft* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int encode_traffic_flow_template_add_packet(AddPacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int encode_traffic_flow_template_replace_packet(ReplacePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);

static int encode_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);
static int encode_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len);

static void dump_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters);
static void dump_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters);

int decode_traffic_flow_template(TrafficFlowTemplate *trafficflowtemplate, uint8_t iei, uint8_t *buffer, uint32_t len)
{
  int decoded = 0;
  int decoded_result = 0;
  uint8_t ielen = 0;

  if (iei > 0) {
    CHECK_IEI_DECODER(iei, *buffer);
    decoded++;
  }

  ielen = *(buffer + decoded);
  decoded++;
  CHECK_LENGTH_DECODER(len - decoded, ielen);
  trafficflowtemplate->tftoperationcode = (*(buffer + decoded) >> 5) & 0x7;
  trafficflowtemplate->ebit = (*(buffer + decoded) >> 4) & 0x1;
  trafficflowtemplate->numberofpacketfilters = *(buffer + decoded) & 0xf;
  decoded++;

  /* Decoding packet filter list */
  if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_DELETE_PACKET) {
    decoded_result = decode_traffic_flow_template_delete_packet(&trafficflowtemplate->packetfilterlist.deletepacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + decoded), len - decoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_CREATE) {
    decoded_result = decode_traffic_flow_template_create_tft(&trafficflowtemplate->packetfilterlist.createtft, trafficflowtemplate->numberofpacketfilters, (buffer + decoded), len - decoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_ADD_PACKET) {
    decoded_result = decode_traffic_flow_template_add_packet(&trafficflowtemplate->packetfilterlist.addpacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + decoded), len - decoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_REPLACE_PACKET) {
    decoded_result = decode_traffic_flow_template_replace_packet(&trafficflowtemplate->packetfilterlist.replacepacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + decoded), len - decoded);
  }

#if defined (NAS_DEBUG)
  dump_traffic_flow_template_xml(trafficflowtemplate, iei);
#endif

  if (decoded_result < 0) {
    return decoded_result;
  }

  return (decoded + decoded_result);
}

int encode_traffic_flow_template(TrafficFlowTemplate *trafficflowtemplate, uint8_t iei, uint8_t *buffer, uint32_t len)
{
  uint8_t *lenPtr;
  uint32_t encoded = 0;
  /* Checking IEI and pointer */
  CHECK_PDU_POINTER_AND_LENGTH_ENCODER(buffer, TRAFFIC_FLOW_TEMPLATE_MINIMUM_LENGTH, len);
#if defined (NAS_DEBUG)
  dump_traffic_flow_template_xml(trafficflowtemplate, iei);
#endif

  if (iei > 0) {
    *buffer = iei;
    encoded++;
  }

  lenPtr  = (buffer + encoded);
  encoded ++;
  *(buffer + encoded) = 0x00 | ((trafficflowtemplate->tftoperationcode & 0x7) << 5) | ((trafficflowtemplate->ebit & 0x1) << 4) | (trafficflowtemplate->numberofpacketfilters & 0xf);
  encoded++;

  /* Encoding packet filter list */
  if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_DELETE_PACKET) {
    encoded += encode_traffic_flow_template_delete_packet(&trafficflowtemplate->packetfilterlist.deletepacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + encoded), len - encoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_CREATE) {
    encoded += encode_traffic_flow_template_create_tft(&trafficflowtemplate->packetfilterlist.createtft, trafficflowtemplate->numberofpacketfilters, (buffer + encoded), len - encoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_ADD_PACKET) {
    encoded += encode_traffic_flow_template_add_packet(&trafficflowtemplate->packetfilterlist.addpacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + encoded), len - encoded);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_REPLACE_PACKET) {
    encoded += encode_traffic_flow_template_replace_packet(&trafficflowtemplate->packetfilterlist.replacepacketfilter, trafficflowtemplate->numberofpacketfilters, (buffer + encoded), len - encoded);
  }

  *lenPtr = encoded - 1 - ((iei > 0) ? 1 : 0);
  return encoded;
}

void dump_traffic_flow_template_xml(TrafficFlowTemplate *trafficflowtemplate, uint8_t iei)
{
  printf("<Traffic Flow Template>\n");

  if (iei > 0)
    /* Don't display IEI if = 0 */
    printf("    <IEI>0x%X</IEI>\n", iei);

  printf("    <TFT operation code>%u</TFT operation code>\n", trafficflowtemplate->tftoperationcode);
  printf("    <E bit>%u</E bit>\n", trafficflowtemplate->ebit);
  printf("    <Number of packet filters>%u</Number of packet filters>\n", trafficflowtemplate->numberofpacketfilters);

  if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_DELETE_PACKET) {
    dump_traffic_flow_template_packet_filter_identifiers(&trafficflowtemplate->packetfilterlist.deletepacketfilter, trafficflowtemplate->numberofpacketfilters);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_CREATE) {
    dump_traffic_flow_template_packet_filters(&trafficflowtemplate->packetfilterlist.createtft, trafficflowtemplate->numberofpacketfilters);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_ADD_PACKET) {
    dump_traffic_flow_template_packet_filters(&trafficflowtemplate->packetfilterlist.addpacketfilter, trafficflowtemplate->numberofpacketfilters);
  } else if (trafficflowtemplate->tftoperationcode == TRAFFIC_FLOW_TEMPLATE_OPCODE_REPLACE_PACKET) {
    dump_traffic_flow_template_packet_filters(&trafficflowtemplate->packetfilterlist.replacepacketfilter, trafficflowtemplate->numberofpacketfilters);
  }

  printf("</Traffic Flow Template>\n");
}

static int decode_traffic_flow_template_delete_packet(DeletePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return decode_traffic_flow_template_packet_filter_identifiers(packetfilter, nbpacketfilters, buffer, len);
}

static int decode_traffic_flow_template_create_tft(CreateNewTft* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return decode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int decode_traffic_flow_template_add_packet(AddPacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return decode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int decode_traffic_flow_template_replace_packet(ReplacePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return decode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int decode_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  int decoded = 0, i;

  for (i = 0; (i < nbpacketfilters) && (len - decoded > 0); i++) {
    /* Packet filter identifier */
    IES_DECODE_U8(buffer, decoded, (*packetfilteridentifiers)[i].identifier);
  }

  return decoded;
}

static int decode_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  int decoded = 0, i, j;

  for (i = 0; (i < nbpacketfilters); i++) {
    if (len - decoded <= 0) {
      /* Mismatch between the number of packet filters subfield,
       * and the number of packet filters in the packet filter list */
      return (TLV_DECODE_VALUE_DOESNT_MATCH);
    }

    /* Initialize the packet filter presence flag indicator */
    (*packetfilters)[i].packetfilter.flags = 0;
    /* Packet filter direction */
    (*packetfilters)[i].direction = *(buffer + decoded) >> 4;
    /* Packet filter identifier */
    (*packetfilters)[i].identifier = *(buffer + decoded) & 0x0f;
    decoded++;
    /* Packet filter evaluation precedence  */
    IES_DECODE_U8(buffer, decoded, (*packetfilters)[i].eval_precedence);
    /* Length of the Packet filter contents field  */
    uint8_t pkflen;
    IES_DECODE_U8(buffer, decoded, pkflen);
    /* Packet filter contents */
    int pkfstart = decoded;

    while (decoded - pkfstart < pkflen) {
      /* Packet filter component type identifier */
      uint8_t component_type;
      IES_DECODE_U8(buffer, decoded, component_type);

      switch (component_type) {
      case TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR:
        /* IPv4 remote address type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG;

        for (j = 0; j < TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE; j++) {
          (*packetfilters)[i].packetfilter.ipv4remoteaddr[j].addr = *(buffer + decoded);
          (*packetfilters)[i].packetfilter.ipv4remoteaddr[j].mask = *(buffer + decoded + TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE);
          decoded++;
        }

        decoded += TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE;
        break;

      case TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR:
        /* IPv6 remote address type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG;

        for (j = 0; j < TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE; j++) {
          (*packetfilters)[i].packetfilter.ipv6remoteaddr[j].addr = *(buffer + decoded);
          (*packetfilters)[i].packetfilter.ipv6remoteaddr[j].mask = *(buffer + decoded + TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE);
          decoded++;
        }

        decoded += TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE;
        break;

      case TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER:
        /* Protocol identifier/Next header type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER_FLAG;
        IES_DECODE_U8(buffer, decoded, (*packetfilters)[i].packetfilter.protocolidentifier_nextheader);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT:
        /* Single local port type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT_FLAG;
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.singlelocalport);
        break;

      case TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE:
        /* Local port range type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE_FLAG;
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.localportrange.lowlimit);
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.localportrange.highlimit);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT:
        /* Single remote port type */
        (*packetfilters)[i].packetfilter.flags |=TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT_FLAG ;
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.singleremoteport);
        break;

      case TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE:
        /* Remote port range type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE_FLAG;
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.remoteportrange.lowlimit);
        IES_DECODE_U16(buffer, decoded, (*packetfilters)[i].packetfilter.remoteportrange.highlimit);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX:
        /* Security parameter index type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX_FLAG;
        IES_DECODE_U32(buffer, decoded, (*packetfilters)[i].packetfilter.securityparameterindex);
        break;

      case TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS:
        /* Type of service/Traffic class type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS_FLAG;
        IES_DECODE_U8(buffer, decoded, (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.value);
        IES_DECODE_U8(buffer, decoded, (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.mask);
        break;

      case TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL:
        /* Flow label type */
        (*packetfilters)[i].packetfilter.flags |= TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL_FLAG;
        IES_DECODE_U24(buffer, decoded, (*packetfilters)[i].packetfilter.flowlabel);
        break;

      default:
        /* Packet filter component type identifier is not valid */
        return (TLV_DECODE_UNEXPECTED_IEI);
        break;
      }
    }
  }

  if (len - decoded != 0) {
    /* Mismatch between the number of packet filters subfield,
     * and the number of packet filters in the packet filter list */
    return (TLV_DECODE_VALUE_DOESNT_MATCH);
  }

  return decoded;
}

static int encode_traffic_flow_template_delete_packet(DeletePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return encode_traffic_flow_template_packet_filter_identifiers(packetfilter, nbpacketfilters, buffer, len);
}

static int encode_traffic_flow_template_create_tft(CreateNewTft* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return encode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int encode_traffic_flow_template_add_packet(AddPacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return encode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int encode_traffic_flow_template_replace_packet(ReplacePacketFilter* packetfilter, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  return encode_traffic_flow_template_packet_filters(packetfilter, nbpacketfilters, buffer, len);
}

static int encode_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  int encoded = 0, i;

  for (i = 0; (i < nbpacketfilters) && (len - encoded > 0); i++) {
    /* Packet filter identifier */
    IES_ENCODE_U8(buffer, encoded, (*packetfilteridentifiers)[i].identifier);
  }

  return encoded;
}

static int encode_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters, uint8_t *buffer, uint32_t len)
{
  int encoded = 0, i, j;

  for (i = 0; (i < nbpacketfilters) && (len - encoded > 0); i++) {
    if (len - encoded <= 0) {
      /* Mismatch between the number of packet filters subfield,
       * and the number of packet filters in the packet filter list */
      return (TLV_DECODE_VALUE_DOESNT_MATCH);
    }

    /* Packet filter identifier and direction */
    IES_ENCODE_U8(buffer, encoded, (0x00 | ((*packetfilters)[i].direction << 4) | ((*packetfilters)[i].identifier)));
    /* Packet filter evaluation precedence  */
    IES_ENCODE_U8(buffer, encoded, (*packetfilters)[i].eval_precedence);
    /* Save address of the Packet filter contents field length */
    uint8_t* pkflenPtr = buffer + encoded;
    encoded++;
    /* Packet filter contents */
    int pkfstart = encoded;
    uint16_t flag = TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG;

    while (flag <= TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL_FLAG) {
      switch ((*packetfilters)[i].packetfilter.flags & flag) {
      case TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG:
        /* IPv4 remote address type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR);

        for (j = 0; j < TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE; j++) {
          *(buffer + encoded) = (*packetfilters)[i].packetfilter.ipv4remoteaddr[j].addr;
          *(buffer + encoded + TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE) = (*packetfilters)[i].packetfilter.ipv4remoteaddr[j].mask;
          encoded++;
        }

        encoded += TRAFFIC_FLOW_TEMPLATE_IPV4_ADDR_SIZE;
        break;

      case TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG:
        /* IPv6 remote address type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR);

        for (j = 0; j < TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE; j++) {
          *(buffer + encoded) = (*packetfilters)[i].packetfilter.ipv6remoteaddr[j].addr;
          *(buffer + encoded + TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE) = (*packetfilters)[i].packetfilter.ipv6remoteaddr[j].mask;
          encoded++;
        }

        encoded += TRAFFIC_FLOW_TEMPLATE_IPV6_ADDR_SIZE;
        break;

      case TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER_FLAG:
        /* Protocol identifier/Next header type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER);
        IES_ENCODE_U8(buffer, encoded, (*packetfilters)[i].packetfilter.protocolidentifier_nextheader);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT_FLAG:
        /* Single local port type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.singlelocalport);
        break;

      case TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE_FLAG:
        /* Local port range type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.localportrange.lowlimit);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.localportrange.highlimit);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT_FLAG:
        /* Single remote port type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.singleremoteport);
        break;

      case TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE_FLAG:
        /* Remote port range type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.remoteportrange.lowlimit);
        IES_ENCODE_U16(buffer, encoded, (*packetfilters)[i].packetfilter.remoteportrange.highlimit);
        break;

      case TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX_FLAG:
        /* Security parameter index type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX);
        IES_ENCODE_U32(buffer, encoded, (*packetfilters)[i].packetfilter.securityparameterindex);
        break;

      case TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS_FLAG:
        /* Type of service/Traffic class type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS);
        IES_ENCODE_U8(buffer, encoded, (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.value);
        IES_ENCODE_U8(buffer, encoded, (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.mask);
        break;

      case TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL_FLAG:
        /* Flow label type */
        IES_ENCODE_U8(buffer, encoded, TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL);
        IES_ENCODE_U24(buffer, encoded, (*packetfilters)[i].packetfilter.flowlabel & 0x000fffff);
        break;

      default:
        break;
      }

      flag = flag << 1;
    }

    /* Length of the Packet filter contents field  */
    *pkflenPtr = encoded - pkfstart;
  }

  return encoded;
}

static void dump_traffic_flow_template_packet_filter_identifiers(PacketFilterIdentifiers* packetfilteridentifiers, uint8_t nbpacketfilters)
{
  int i;
  printf("    <Packet filter list>\n");

  for (i = 0; i < nbpacketfilters; i++) {
    printf("        <Identifier>%u</Identifier>\n", (*packetfilteridentifiers)[i].identifier);
  }

  printf("    </Packet filter list>\n");
}

static void dump_traffic_flow_template_packet_filters(PacketFilters* packetfilters, uint8_t nbpacketfilters)
{
  int i;
  printf("    <Packet filter list>\n");

  for (i = 0; i < nbpacketfilters; i++) {
    printf("        <Identifier>%u</Identifier>\n", (*packetfilters)[i].identifier);
    printf("        <Direction>%u</Direction>\n", (*packetfilters)[i].direction);
    printf("        <Evaluation precedence>%u</Evaluation precedence>\n", (*packetfilters)[i].eval_precedence);
    printf("        <Packet filter>\n");

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG) {
      printf("            <IPv4 remote address>%u.%u.%u.%u</IPv4 remote address>\n", (*packetfilters)[i].packetfilter.ipv4remoteaddr[0].addr, (*packetfilters)[i].packetfilter.ipv4remoteaddr[1].addr,
             (*packetfilters)[i].packetfilter.ipv4remoteaddr[2].addr, (*packetfilters)[i].packetfilter.ipv4remoteaddr[3].addr);
      printf("            <IPv4 remote address mask>%u.%u.%u.%u</IPv4 remote address mask>\n", (*packetfilters)[i].packetfilter.ipv4remoteaddr[0].mask,
             (*packetfilters)[i].packetfilter.ipv4remoteaddr[1].mask, (*packetfilters)[i].packetfilter.ipv4remoteaddr[2].mask, (*packetfilters)[i].packetfilter.ipv4remoteaddr[3].mask);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG) {
      printf("            <Ipv6 remote address>%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x</Ipv6 remote address>\n", (*packetfilters)[i].packetfilter.ipv6remoteaddr[0].addr,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[1].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[2].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[3].addr,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[4].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[5].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[6].addr,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[7].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[8].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[9].addr,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[10].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[11].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[12].addr,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[13].addr, (*packetfilters)[i].packetfilter.ipv6remoteaddr[14].addr, (*packetfilters)[i].packetfilter.
             ipv6remoteaddr[15].addr);
      printf("            <Ipv6 remote address mask>%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x</Ipv6 remote address mask>\n", (*packetfilters)[i].packetfilter.ipv6remoteaddr[0].mask,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[1].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[2].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[3].mask,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[4].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[5].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[6].mask,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[7].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[8].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[9].mask,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[10].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[11].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[12].mask,
             (*packetfilters)[i].packetfilter.ipv6remoteaddr[13].mask, (*packetfilters)[i].packetfilter.ipv6remoteaddr[14].mask, (*packetfilters)[i].
             packetfilter.ipv6remoteaddr[15].mask);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER_FLAG) {
      printf("            <Protocol identifier - Next header type>%u</Protocol identifier - Next header type>\n", (*packetfilters)[i].packetfilter.protocolidentifier_nextheader);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT_FLAG) {
      printf("            <Single local port>%u</Single local port>\n", (*packetfilters)[i].packetfilter.singlelocalport);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE_FLAG) {
      printf("            <Local port range low limit>%u</Local port range low limit>\n", (*packetfilters)[i].packetfilter.localportrange.lowlimit);
      printf("            <Local port range high limit>%u</Local port range high limit>\n", (*packetfilters)[i].packetfilter.localportrange.highlimit);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT_FLAG) {
      printf("            <Single remote port>%u</Single remote port>\n", (*packetfilters)[i].packetfilter.singleremoteport);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE_FLAG) {
      printf("            <Remote port range low limit>%u</Remote port range low limit>\n", (*packetfilters)[i].packetfilter.remoteportrange.lowlimit);
      printf("            <Remote port range high limit>%u</Remote port range high limit>\n", (*packetfilters)[i].packetfilter.remoteportrange.highlimit);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX_FLAG) {
      printf("            <Security parameter index>%u</Security parameter index>\n", (*packetfilters)[i].packetfilter.securityparameterindex);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS_FLAG) {
      printf("            <Type of service - Traffic class>%u</Type of service - Traffic class>\n", (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.value);
      printf("            <Type of service - Traffic class mask>%u</Type of service - Traffic class mask>\n", (*packetfilters)[i].packetfilter.typdeofservice_trafficclass.mask);
    }

    if ((*packetfilters)[i].packetfilter.flags & TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL_FLAG) {
      printf("            <Flow label>%u</Flow label>\n", (*packetfilters)[i].packetfilter.flowlabel);
    }

    printf("        </Packet filter>\n");
  }

  printf("    </Packet filter list>\n");
}

