/*----------------------------------------------------------------------------*
 *                                                                            *
 *                              n w - g t p v 2 c                             *
 *    G P R S   T u n n e l i n g    P r o t o c o l   v 2 c    S t a c k     *
 *                                                                            *
 *                                                                            *
 * Copyright (c) 2010-2011 Amit Chawre                                        *
 * 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.    *
 * 3. The name of the author may not be used to endorse or promote products   *
 *    derived from this software without specific prior written permission.   *
 *                                                                            *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.          *
 *----------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <inttypes.h>

#include "NwTypes.h"
#include "NwLog.h"
#include "NwUtils.h"
#include "NwGtpv2cLog.h"
#include "NwGtpv2c.h"
#include "NwGtpv2cPrivate.h"
#include "NwGtpv2cIe.h"
#include "NwGtpv2cMsg.h"

#ifdef __cplusplus
extern "C" {
#endif


/*----------------------------------------------------------------------------*
 *                     P R I V A T E     F U N C T I O N S                    *
 *----------------------------------------------------------------------------*/

static NwGtpv2cMsgT* gpGtpv2cMsgPool = NULL;

/*----------------------------------------------------------------------------*
 *                       P U B L I C   F U N C T I O N S                      *
 *----------------------------------------------------------------------------*/

NwRcT
nwGtpv2cMsgNew( NW_IN NwGtpv2cStackHandleT hGtpcStackHandle,
                NW_IN uint8_t     teidPresent,
                NW_IN uint8_t     msgType,
                NW_IN uint32_t    teid,
                NW_IN uint32_t    seqNum,
                NW_OUT NwGtpv2cMsgHandleT *phMsg)
{
  NwGtpv2cStackT* pStack = (NwGtpv2cStackT*) hGtpcStackHandle;
  NwGtpv2cMsgT *pMsg;

  NW_ASSERT(pStack);

  if(gpGtpv2cMsgPool) {
    pMsg = gpGtpv2cMsgPool;
    gpGtpv2cMsgPool = gpGtpv2cMsgPool->next;
  } else {
    NW_GTPV2C_MALLOC(pStack, sizeof(NwGtpv2cMsgT), pMsg, NwGtpv2cMsgT*);
  }

  if(pMsg) {
    pMsg->version       = NW_GTP_VERSION;
    pMsg->teidPresent   = teidPresent;
    pMsg->msgType       = msgType;
    pMsg->teid          = teid;
    pMsg->seqNum        = seqNum;
    pMsg->msgLen        = (NW_GTPV2C_EPC_SPECIFIC_HEADER_SIZE - (teidPresent ? 0 : 4));

    pMsg->groupedIeEncodeStack.top = 0;
    pMsg->hStack        = hGtpcStackHandle;

    *phMsg = (NwGtpv2cMsgHandleT) pMsg;
    NW_LOG(pStack, NW_LOG_LEVEL_DEBG, "Created message %p!", pMsg);
    return NW_OK;
  }

  return NW_FAILURE;
}

NwRcT
nwGtpv2cMsgFromBufferNew( NW_IN NwGtpv2cStackHandleT hGtpcStackHandle,
                          NW_IN uint8_t* pBuf,
                          NW_IN uint32_t bufLen,
                          NW_OUT NwGtpv2cMsgHandleT *phMsg)
{
  NwGtpv2cStackT* pStack = (NwGtpv2cStackT*) hGtpcStackHandle;
  NwGtpv2cMsgT *pMsg;

  NW_ASSERT(pStack);

  if(gpGtpv2cMsgPool) {
    pMsg = gpGtpv2cMsgPool;
    gpGtpv2cMsgPool = gpGtpv2cMsgPool->next;
  } else {
    NW_GTPV2C_MALLOC(pStack, sizeof(NwGtpv2cMsgT), pMsg, NwGtpv2cMsgT*);
  }

  if(pMsg) {
    *phMsg = (NwGtpv2cMsgHandleT) pMsg;
    memcpy(pMsg->msgBuf, pBuf, bufLen);
    pMsg->msgLen = bufLen;

    pMsg->version       = ((*pBuf) & 0xE0) >> 5;
    pMsg->teidPresent   = ((*pBuf) & 0x08) >> 3;
    pBuf++;

    pMsg->msgType       = *(pBuf);
    pBuf += 3;

    if(pMsg->teidPresent) {
      pMsg->teid          = ntohl(*((uint32_t*)(pBuf)));
      pBuf += 4;
    }

    memcpy(((uint8_t*)&pMsg->seqNum) + 1, pBuf, 3);
    pMsg->seqNum  = ntohl(pMsg->seqNum);

    pMsg->hStack        = hGtpcStackHandle;
    NW_LOG(pStack, NW_LOG_LEVEL_DEBG, "Created message %p!", pMsg);
    return NW_OK;
  }

  return NW_FAILURE;
}

NwRcT
nwGtpv2cMsgDelete( NW_IN NwGtpv2cStackHandleT hGtpcStackHandle,
                   NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cStackT* pStack = (NwGtpv2cStackT*) hGtpcStackHandle;
  NW_LOG(pStack, NW_LOG_LEVEL_DEBG, "Purging message %"PRIxPTR"!", hMsg);

  ((NwGtpv2cMsgT*)hMsg)->next = gpGtpv2cMsgPool;
  gpGtpv2cMsgPool = (NwGtpv2cMsgT*) hMsg;

  return NW_OK;
}

/**
 * Set TEID for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 * @param[in] teid: TEID value.
 */

NwRcT
nwGtpv2cMsgSetTeid(NW_IN NwGtpv2cMsgHandleT hMsg, uint32_t teid)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  thiz->teid = teid;
  return NW_OK;
}

/**
 * Set TEID present flag for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 * @param[in] teidPesent: Flag boolean value.
 */

NwRcT
nwGtpv2cMsgSetTeidPresent(NW_IN NwGtpv2cMsgHandleT hMsg, NwBoolT teidPresent)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  thiz->teidPresent = teidPresent;
  return NW_OK;
}

/**
 * Set sequence for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 * @param[in] seqNum: Flag boolean value.
 */

NwRcT
nwGtpv2cMsgSetSeqNumber(NW_IN NwGtpv2cMsgHandleT hMsg, uint32_t seqNum)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  thiz->seqNum = seqNum;
  return NW_OK;
}

/**
 * Get TEID present for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 */

uint32_t
nwGtpv2cMsgGetTeid(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  return (thiz->teid);
}

/**
 * Get TEID present for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 */

NwBoolT
nwGtpv2cMsgGetTeidPresent(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  return (thiz->teidPresent);
}

/**
 * Get sequence number for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 */

uint32_t
nwGtpv2cMsgGetSeqNumber(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  return (thiz->seqNum);
}

/**
 * Get msg type for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 */

uint32_t
nwGtpv2cMsgGetMsgType(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  return (thiz->msgType);
}

/**
 * Get msg type for gtpv2c message.
 *
 * @param[in] hMsg : Message handle.
 */

uint32_t
nwGtpv2cMsgGetLength(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  return (thiz->msgLen);
}


NwRcT
nwGtpv2cMsgAddIeTV1(NW_IN NwGtpv2cMsgHandleT hMsg,
                    NW_IN uint8_t       type,
                    NW_IN uint8_t       instance,
                    NW_IN uint8_t       value)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv1T *pIe;

  pIe = (NwGtpv2cIeTv1T*) (pMsg->msgBuf + pMsg->msgLen);

  pIe->t        = type;
  pIe->l        = htons(0x0001);
  pIe->i        = instance & 0x00ff;
  pIe->v        = value;

  pMsg->msgLen += sizeof(NwGtpv2cIeTv1T);

  return NW_OK;
}

NwRcT
nwGtpv2cMsgAddIeTV2(NW_IN NwGtpv2cMsgHandleT hMsg,
                    NW_IN uint8_t       type,
                    NW_IN uint8_t       instance,
                    NW_IN uint16_t      value)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv2T *pIe;

  pIe = (NwGtpv2cIeTv2T*) (pMsg->msgBuf + pMsg->msgLen);

  pIe->t        = type;
  pIe->l        = htons(0x0002);
  pIe->i        = instance & 0x00ff;
  pIe->v        = htons(value);

  pMsg->msgLen += sizeof(NwGtpv2cIeTv2T);

  return NW_OK;
}

NwRcT
nwGtpv2cMsgAddIeTV4(NW_IN NwGtpv2cMsgHandleT hMsg,
                    NW_IN uint8_t       type,
                    NW_IN uint8_t       instance,
                    NW_IN uint32_t      value)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv4T *pIe;

  pIe = (NwGtpv2cIeTv4T*) (pMsg->msgBuf + pMsg->msgLen);

  pIe->t        = type;
  pIe->l        = htons(0x0004);
  pIe->i        = instance & 0x00ff;
  pIe->v        = htonl(value);

  pMsg->msgLen += sizeof(NwGtpv2cIeTv4T);

  return NW_OK;
}

NwRcT
nwGtpv2cMsgAddIe(NW_IN NwGtpv2cMsgHandleT hMsg,
                 NW_IN uint8_t       type,
                 NW_IN uint16_t      length,
                 NW_IN uint8_t       instance,
                 NW_IN uint8_t*      pVal)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;

  pIe = (NwGtpv2cIeTlvT*) (pMsg->msgBuf + pMsg->msgLen);

  pIe->t        = type;
  pIe->l        = htons(length);
  pIe->i        = instance & 0x00ff;

  memcpy(((uint8_t*)pIe) + 4, pVal, length);
  pMsg->msgLen += (4 + length);

  return NW_OK;
}

NwRcT
nwGtpv2cMsgGroupedIeStart(NW_IN NwGtpv2cMsgHandleT hMsg,
                          NW_IN uint8_t       type,
                          NW_IN uint8_t       instance)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;

  pIe = (NwGtpv2cIeTlvT*) (pMsg->msgBuf + pMsg->msgLen);

  pIe->t        = type;
  pIe->i        = instance & 0x00ff;
  pMsg->msgLen += (4);
  pIe->l        = (pMsg->msgLen);

  NW_ASSERT(pMsg->groupedIeEncodeStack.top < NW_GTPV2C_MAX_GROUPED_IE_DEPTH);

  pMsg->groupedIeEncodeStack.pIe[pMsg->groupedIeEncodeStack.top] = pIe;
  pMsg->groupedIeEncodeStack.top++;

  return NW_OK;
}

NwRcT
nwGtpv2cMsgGroupedIeEnd(NW_IN NwGtpv2cMsgHandleT hMsg)
{
  NwGtpv2cMsgT *pMsg = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;

  NW_ASSERT(pMsg->groupedIeEncodeStack.top > 0);

  pMsg->groupedIeEncodeStack.top--;
  pIe = pMsg->groupedIeEncodeStack.pIe[pMsg->groupedIeEncodeStack.top];

  pIe->l       = htons(pMsg->msgLen - pIe->l);

  return NW_OK;
}

NwRcT
nwGtpv2cMsgAddIeCause(NW_IN NwGtpv2cMsgHandleT hMsg,
                      NW_IN uint8_t instance,
                      NW_IN uint8_t causeValue,
                      NW_IN uint8_t bitFlags,
                      NW_IN uint8_t offendingIeType,
                      NW_IN uint8_t offendingIeInstance)
{
  uint8_t causeBuf[8];

  causeBuf[0] = causeValue;
  causeBuf[1] = bitFlags;

  if(offendingIeType) {
    causeBuf[2] = offendingIeType;
    causeBuf[3] = 0;
    causeBuf[4] = 0;
    causeBuf[5] = (offendingIeInstance & 0x0f);
  }

  return (nwGtpv2cMsgAddIe(hMsg, NW_GTPV2C_IE_CAUSE, (offendingIeType? 6 : 2), instance, causeBuf));
}

NwRcT
nwGtpv2cMsgAddIeFteid(NW_IN NwGtpv2cMsgHandleT hMsg,
                      NW_IN uint8_t       instance,
                      NW_IN uint8_t       ifType,
                      NW_IN uint32_t      teidOrGreKey,
                      NW_IN uint32_t      ipv4Addr,
                      NW_IN uint8_t*      pIpv6Addr)
{

  uint8_t fteidBuf[32];
  uint8_t *pFteidBuf = fteidBuf;

  fteidBuf[0] = (ifType & 0x1F);
  pFteidBuf++;

  *((uint32_t*)(pFteidBuf)) = htonl((teidOrGreKey));
  pFteidBuf += 4;

  if(ipv4Addr) {
    fteidBuf[0] |= (0x01 << 7);
    *((uint32_t*)(pFteidBuf)) = htonl(ipv4Addr);
    pFteidBuf += 4;
  }

  if(pIpv6Addr) {
    fteidBuf[0] |= (0x01 << 6);
    memcpy((pFteidBuf), pIpv6Addr, 16);
    pFteidBuf += 16;
  }

  return (nwGtpv2cMsgAddIe(hMsg, NW_GTPV2C_IE_FTEID, (pFteidBuf - fteidBuf), instance, fteidBuf));
}


NwBoolT
nwGtpv2cMsgIsIePresent(NW_IN NwGtpv2cMsgHandleT hMsg,
                       NW_IN uint8_t type,
                       NW_IN uint8_t instance)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;

  if((NwGtpv2cIeTv1T*) thiz->pIe[type][instance])
    return NW_TRUE;

  return NW_FALSE;
}

NwRcT
nwGtpv2cMsgGetIeTV1(NW_IN NwGtpv2cMsgHandleT hMsg,
                    NW_IN uint8_t type,
                    NW_IN uint8_t instance,
                    NW_OUT uint8_t* pVal)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv1T *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTv1T*) thiz->pIe[type][instance];

    if(ntohs(pIe->l) != 0x01)
      return NW_GTPV2C_IE_INCORRECT;

    if(pVal) *pVal = pIe->v;

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeTV2( NW_IN NwGtpv2cMsgHandleT hMsg,
                     NW_IN uint8_t type,
                     NW_IN uint8_t instance,
                     NW_OUT uint16_t* pVal)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv2T *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTv2T*) thiz->pIe[type][instance];

    if(ntohs(pIe->l) != 0x02)
      return NW_GTPV2C_IE_INCORRECT;

    if(pVal) *pVal = ntohs(pIe->v);

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeTV4( NW_IN NwGtpv2cMsgHandleT hMsg,
                     NW_IN uint8_t type,
                     NW_IN uint8_t instance,
                     NW_OUT uint32_t* pVal)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv4T *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTv4T*) thiz->pIe[type][instance];

    if(ntohs(pIe->l) != 0x04)
      return NW_GTPV2C_IE_INCORRECT;

    if(pVal) *pVal = ntohl(pIe->v);

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeTV8( NW_IN NwGtpv2cMsgHandleT hMsg,
                     NW_IN uint8_t type,
                     NW_IN uint8_t instance,
                     NW_OUT uint64_t* pVal)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTv8T *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTv8T*) thiz->pIe[type][instance];

    if(ntohs(pIe->l) != 0x08)
      return NW_GTPV2C_IE_INCORRECT;

    if(pVal) *pVal = NW_NTOHLL((pIe->v));

    return NW_OK;
  }

  NW_LOG(thiz->hStack, NW_LOG_LEVEL_ERRO, "Cannot retrieve IE of type %u instance %u !", type, instance);
  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeTlv( NW_IN NwGtpv2cMsgHandleT hMsg,
                     NW_IN uint8_t type,
                     NW_IN uint8_t instance,
                     NW_IN uint16_t maxLen,
                     NW_OUT uint8_t* pVal,
                     NW_OUT uint16_t* pLen)
{
  NwGtpv2cMsgT *thiz  = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTlvT*) thiz->pIe[type][instance];

    if(ntohs(pIe->l) <= maxLen) {
      if(pVal) memcpy(pVal, ((uint8_t*) pIe) + 4, ntohs(pIe->l));

      if(pLen) *pLen = ntohs(pIe->l);

      return NW_OK;
    }
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeTlvP( NW_IN NwGtpv2cMsgHandleT hMsg,
                      NW_IN uint8_t type,
                      NW_IN uint8_t instance,
                      NW_OUT uint8_t** ppVal,
                      NW_OUT uint16_t* pLen)
{
  NwGtpv2cMsgT *thiz  = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;
  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[type][instance]) {
    pIe = (NwGtpv2cIeTlvT*) thiz->pIe[type][instance];

    if(ppVal) *ppVal = ((uint8_t*) pIe) + 4;

    if(pLen)  *pLen  = ntohs(pIe->l);

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeCause(NW_IN NwGtpv2cMsgHandleT hMsg,
                      NW_IN  uint8_t       instance,
                      NW_OUT uint8_t*      causeValue,
                      NW_OUT uint8_t*      flags,
                      NW_OUT uint8_t*      offendingIeType,
                      NW_OUT uint8_t*      offendingIeInstance)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;

  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[NW_GTPV2C_IE_CAUSE][instance]) {
    pIe = (NwGtpv2cIeTlvT*) thiz->pIe[NW_GTPV2C_IE_CAUSE][instance];
    *causeValue = *((uint8_t*)(((uint8_t*)pIe) + 4));
    *flags      = *((uint8_t*)(((uint8_t*)pIe) + 5));

    if(pIe->l == 6) {
      *offendingIeType    = *((uint8_t*)(((uint8_t*)pIe) + 6));
      *offendingIeType    = *((uint8_t*)(((uint8_t*)pIe) + 8));
    }

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgGetIeFteid(NW_IN NwGtpv2cMsgHandleT hMsg,
                      NW_IN  uint8_t       instance,
                      NW_OUT uint8_t*      ifType,
                      NW_OUT uint32_t*     teidOrGreKey,
                      NW_OUT uint32_t*     ipv4Addr,
                      NW_OUT uint8_t*      pIpv6Addr)
{
  NwGtpv2cMsgT *thiz = (NwGtpv2cMsgT*) hMsg;
  NwGtpv2cIeTlvT *pIe;

  NW_ASSERT(instance <= NW_GTPV2C_IE_INSTANCE_MAXIMUM);

  if(thiz->isIeValid[NW_GTPV2C_IE_FTEID][instance]) {
    pIe = (NwGtpv2cIeTlvT*) thiz->pIe[NW_GTPV2C_IE_FTEID][instance];
    uint8_t flags;
    uint8_t* pIeValue     = ((uint8_t*) pIe) + 4;
    flags               = (*pIeValue) & 0xE0;
    *ifType             = (*pIeValue) & 0x1F;
    pIeValue += 1;

    *teidOrGreKey       = ntohl(*((uint32_t*)(pIeValue)));
    pIeValue += 4;

    if(flags & 0x80) {
      *ipv4Addr           = ntohl(*((uint32_t*)(pIeValue)));
      pIeValue += 4;
    }

    return NW_OK;
  }

  return NW_GTPV2C_IE_MISSING;
}

NwRcT
nwGtpv2cMsgHexDump(NwGtpv2cMsgHandleT hMsg, FILE* fp)
{

  NwGtpv2cMsgT* pMsg = (NwGtpv2cMsgT*) hMsg;
  uint8_t* data = pMsg->msgBuf;
  uint32_t size = pMsg->msgLen;

  unsigned char *p = (unsigned char*)data;
  unsigned char c;
  int n;
  char bytestr[4] = {0};
  char addrstr[10] = {0};
  char hexstr[ 16*3 + 5] = {0};
  char charstr[16*1 + 5] = {0};
  fprintf((FILE*)fp, "\n");

  for(n=1; n<=size; n++) {
    if (n%16 == 1) {
      /* store address for this line */
      snprintf(addrstr, sizeof(addrstr), "%.4lx",
               ((unsigned long)p-(unsigned long)data) );
    }

    c = *p;

    if (isalnum(c) == 0) {
      c = '.';
    }

    /* store hex str (for left side) */
    snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
    strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);

    /* store char str (for right side) */
    snprintf(bytestr, sizeof(bytestr), "%c", c);
    strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);

    if(n%16 == 0) {
      /* line completed */
      fprintf((FILE*)fp, "[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
      hexstr[0] = 0;
      charstr[0] = 0;
    } else if(n%8 == 0) {
      /* half line: add whitespaces */
      strncat(hexstr, "  ", sizeof(hexstr)-strlen(hexstr)-1);
      strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
    }

    p++; /* next byte */
  }

  if (strlen(hexstr) > 0) {
    /* print rest of buffer if not empty */
    fprintf((FILE*)fp, "[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);

  }

  fprintf((FILE*)fp, "\n");

  return NW_OK;
}

#ifdef __cplusplus
}
#endif



/*--------------------------------------------------------------------------*
 *                          E N D   O F   F I L E                           *
 *--------------------------------------------------------------------------*/