/*
 *  OsmoGGSN - Gateway GPRS Support Node
 *  Copyright (C) 2002 Mondru AB.
 *
 *  The contents of this file may be used under the terms of the GNU
 *  General Public License Version 2, provided that the above copyright
 *  notice and this permission notice is included in all copies or
 *  substantial portions of the software.
 *
 */

/*
 * gtpie.c: Contains functions to encapsulate and decapsulate GTP
 * information elements
 *
 *
 * Encapsulation
 * - gtpie_tlv, gtpie_tv0, gtpie_tv1, gtpie_tv2 ... Adds information
 * elements to a buffer.
 *
 * Decapsulation
 *  - gtpie_decaps: Returns array with pointers to information elements.
 *  - getie_getie: Returns the pointer of a particular element.
 *  - gtpie_gettlv: Copies tlv information element. Return 0 on success.
 *  - gtpie_gettv: Copies tv information element. Return 0 on success.
 *
 */

#include <../config.h>

#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>

#include <osmocom/gtp/gtpie.h>

/*! Encode a TLV type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] l Length of value \a v in bytes
 *  \param[in] v Pointer to input value
 *  \returns 0 on success, 1 on error */
int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      int l, const void *v)
{
	if ((*length + 3 + l) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tlv.t = hton8(t);
	((union gtpie_member *)(p + *length))->tlv.l = hton16(l);
	memcpy((void *)(p + *length + 3), v, l);
	*length += 3 + l;
	return 0;
}

/*! Encode a TV0 (Tag + value) type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] l Length of value \a v in bytes
 *  \param[in] v Pointer to input value
 *  \returns 0 on success, 1 on error */
int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      int l, const uint8_t * v)
{
	if ((*length + 1 + l) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tv0.t = hton8(t);
	memcpy((void *)(p + *length + 1), v, l);
	*length += 1 + l;
	return 0;
}

/*! Encode a TV1 (Tag + 8bit value) type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] v Input value
 *  \returns 0 on success, 1 on error */
int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      uint8_t v)
{
	if ((*length + 2) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tv1.t = hton8(t);
	((union gtpie_member *)(p + *length))->tv1.v = hton8(v);
	*length += 2;
	return 0;
}

/*! Encode a TV2 (Tag + 16bit value) type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] v Input value
 *  \returns 0 on success, 1 on error */
int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      uint16_t v)
{
	if ((*length + 3) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tv2.t = hton8(t);
	((union gtpie_member *)(p + *length))->tv2.v = hton16(v);
	*length += 3;
	return 0;
}

/*! Encode a TV4 (Tag + 32bit value) type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] v Input value
 *  \returns 0 on success, 1 on error */
int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      uint32_t v)
{
	if ((*length + 5) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tv4.t = hton8(t);
	((union gtpie_member *)(p + *length))->tv4.v = hton32(v);
	*length += 5;
	return 0;
}

/*! Encode a TV8 (Tag + 64bit value) type Information Element.
 *  \param[inout] p Pointer to output packet to which IE is appended
 *  \param[inout] length Up to which byte length is \a p used/filled
 *  \param[in] size Total size of \a p in bytes
 *  \param[in] t Tag / Information Element Identifier
 *  \param[in] v Input value
 *  \returns 0 on success, 1 on error */
int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
	      uint64_t v)
{
	if ((*length + 9) >= size)
		return 1;
	((union gtpie_member *)(p + *length))->tv8.t = hton8(t);
	((union gtpie_member *)(p + *length))->tv8.v = hton64(v);
	*length += 9;
	return 0;
}

/*! Obtain a GTP IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \returns index into \a ie on success; -1 if not found */
int gtpie_getie(union gtpie_member *ie[], int type, int instance)
{
	int j;
	for (j = 0; j < GTPIE_SIZE; j++) {
		if ((ie[j] != 0) && (ie[j]->t == type)) {
			if (instance-- == 0)
				return j;
		}
	}
	return -1;
}

/*! Determine if IE for a given tag/IEI exists in a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \returns 1 if IEI instance present in \a ie; 0 if not */
int gtpie_exist(union gtpie_member *ie[], int type, int instance)
{
	int j;
	for (j = 0; j < GTPIE_SIZE; j++) {
		if ((ie[j] != 0) && (ie[j]->t == type)) {
			if (instance-- == 0)
				return 1;
		}
	}
	return 0;
}

/*! Obtain Value of TLV-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[out] length Length of IE
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \param[in] size Size of \a dst in bytes
 *  \returns 0 on sucess; EOF in case value is larger than \a size */
int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
		 unsigned int *length, void *dst, unsigned int size)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0) {
		*length = ntoh16(ie[ien]->tlv.l);
		if (*length <= size)
			memcpy(dst, ie[ien]->tlv.v, *length);
		else
			return EOF;
	}
	return 0;
}

/*! Obtain Value of TV0-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \param[in] size Size of value in bytes
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
		 void *dst, unsigned int size)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0)
		memcpy(dst, ie[ien]->tv0.v, size);
	else
		return EOF;
	return 0;
}

/*! Obtain Value of TV1-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
		 uint8_t * dst)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0)
		*dst = ntoh8(ie[ien]->tv1.v);
	else
		return EOF;
	return 0;
}

/*! Obtain Value of TV2-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
		 uint16_t * dst)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0)
		*dst = ntoh16(ie[ien]->tv2.v);
	else
		return EOF;
	return 0;
}

/*! Obtain Value of TV4-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
		 uint32_t * dst)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0)
		*dst = ntoh32(ie[ien]->tv4.v);
	else
		return EOF;
	return 0;
}

/*! Obtain Value of TV8-type IE for a given tag/IEI from a list/array.
 *  \param[in] ie Array of GTPIE
 *  \param[in] type Tag/IEI for which we're looking
 *  \param[in] instance Instance (number of occurence) of this IEI
 *  \param[inout] dst Caller-allocated buffer where to store value
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
		 uint64_t * dst)
{
	int ien;
	ien = gtpie_getie(ie, type, instance);
	if (ien >= 0)
		*dst = ntoh64(ie[ien]->tv8.v);
	else
		return EOF;
	return 0;
}

/*! Parse an incoming GTP packet into its Information Elements.
 *  \param[out] ie Caller-allocated Array of GTPIE
 *  \param[in] version GTP protocol version
 *  \param[in] pack Pointer to raw GTP packet (payload part)
 *  \param[in] len Length of \a pack in bytes
 *  \returns 0 on sucess; EOF in case IE not found */
int gtpie_decaps(union gtpie_member *ie[], int version, const void *pack,
		 unsigned len)
{
	int i;
	int j = 0;
	const unsigned char *p;
	const unsigned char *end;

	end = (unsigned char *)pack + len;
	p = pack;

	memset(ie, 0, sizeof(union gtpie_member *) * GTPIE_SIZE);

	while ((p < end) && (j < GTPIE_SIZE)) {
		if (GTPIE_DEBUG) {
			printf("The packet looks like this:\n");
			for (i = 0; i < (end - p); i++) {
				printf("%02x ",
				       (unsigned char)*(char *)(p + i));
				if (!((i + 1) % 16))
					printf("\n");
			};
			printf("\n");
		}

		switch (*p) {
		case GTPIE_CAUSE:	/* TV GTPIE types with value length 1 */
		case GTPIE_REORDER:
		case GTPIE_MAP_CAUSE:
		case GTPIE_MS_VALIDATED:
		case GTPIE_RECOVERY:
		case GTPIE_SELECTION_MODE:
		case GTPIE_TEARDOWN:
		case GTPIE_NSAPI:
		case GTPIE_RANAP_CAUSE:
		case GTPIE_RP_SMS:
		case GTPIE_RP:
		case GTPIE_MS_NOT_REACH:
		case GTPIE_BCM:
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE TV1 found. Type %d, value %d\n",
					     ie[j]->tv1.t, ie[j]->tv1.v);
				p += 1 + 1;
				j++;
			}
			break;
		case GTPIE_FL_DI:	/* TV GTPIE types with value length 2 or 4 */
		case GTPIE_FL_C:
			if (version != 0) {
				if (j < GTPIE_SIZE) {	/* GTPIE_TEI_DI & GTPIE_TEI_C with length 4 */
					/* case GTPIE_TEI_DI: gtp1 */
					/* case GTPIE_TEI_C:  gtp1 */
					ie[j] = (union gtpie_member *)p;
					if (GTPIE_DEBUG)
						printf
						    ("GTPIE TV 4 found. Type %d, value %d\n",
						     ie[j]->tv4.t,
						     ie[j]->tv4.v);
					p += 1 + 4;
					j++;
				}
				break;
			}
		case GTPIE_PFI:	/* TV GTPIE types with value length 2 */
		case GTPIE_CHARGING_C:
		case GTPIE_TRACE_REF:
		case GTPIE_TRACE_TYPE:
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE TV2 found. Type %d, value %d\n",
					     ie[j]->tv2.t, ie[j]->tv2.v);
				p += 1 + 2;
				j++;
			}
			break;
		case GTPIE_QOS_PROFILE0:	/* TV GTPIE types with value length 3 */
		case GTPIE_P_TMSI_S:
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE TV 3 found. Type %d, value %d, %d, %d\n",
					     ie[j]->tv0.t, ie[j]->tv0.v[0],
					     ie[j]->tv0.v[1], ie[j]->tv0.v[2]);
				p += 1 + 3;
				j++;
			}
			break;
		case GTPIE_TLLI:	/* TV GTPIE types with value length 4 */
		case GTPIE_P_TMSI:
		case GTPIE_CHARGING_ID:
			/* case GTPIE_TEI_DI: Handled by GTPIE_FL_DI */
			/* case GTPIE_TEI_C:  Handled by GTPIE_FL_DI */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE TV 4 found. Type %d, value %d\n",
					     ie[j]->tv4.t, ie[j]->tv4.v);
				p += 1 + 4;
				j++;
			}
			break;
		case GTPIE_TEI_DII:	/* TV GTPIE types with value length 5 */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf("GTPIE TV 5 found. Type %d\n",
					       ie[j]->tv0.t);
				p += 1 + 5;
				j++;
			}
			break;
		case GTPIE_RAB_CONTEXT:	/* TV GTPIE types with value length 7 */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf("GTPIE TV 7 found. Type %d\n",
					       ie[j]->tv0.t);
				p += 1 + 7;
				j++;
			}
			break;
		case GTPIE_IMSI:	/* TV GTPIE types with value length 8 */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE_IMSI - GTPIE TV 8 found. Type %d, value 0x%llx\n",
					     ie[j]->tv0.t, ie[j]->tv8.v);
				p += 1 + 8;
				j++;
			}
			break;
		case GTPIE_RAI:	/* TV GTPIE types with value length 6 */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE_RAI - GTPIE TV 6 found. Type %d, value 0x%llx\n",
					     ie[j]->tv0.t, ie[j]->tv8.v);
				p += 1 + 6;
				j++;
			}
			break;
		case GTPIE_AUTH_TRIPLET:	/* TV GTPIE types with value length 28 */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf("GTPIE TV 28 found. Type %d\n",
					       ie[j]->tv0.t);
				p += 1 + 28;
				j++;
			}
			break;
		case GTPIE_EXT_HEADER_T:	/* GTP extension header */
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf
					    ("GTPIE GTP extension header found. Type %d\n",
					     ie[j]->ext.t);
				p += 2 + ntoh8(ie[j]->ext.l);
				j++;
			}
			break;
		case GTPIE_EUA:	/* TLV GTPIE types with variable length */
		case GTPIE_MM_CONTEXT:
		case GTPIE_PDP_CONTEXT:
		case GTPIE_APN:
		case GTPIE_PCO:
		case GTPIE_GSN_ADDR:
		case GTPIE_MSISDN:
		case GTPIE_QOS_PROFILE:
		case GTPIE_AUTH_QUINTUP:
		case GTPIE_TFT:
		case GTPIE_TARGET_INF:
		case GTPIE_UTRAN_TRANS:
		case GTPIE_RAB_SETUP:
		case GTPIE_TRIGGER_ID:
		case GTPIE_OMC_ID:
		case GTPIE_RAN_T_CONTAIN:
		case GTPIE_PDP_CTX_PRIO:
		case GTPIE_ADDL_RAB_S_I:
		case GTPIE_SGSN_NUMBER:
		case GTPIE_COMMON_FLAGS:
		case GTPIE_APN_RESTR:
		case GTPIE_R_PRIO_LCS:
		case GTPIE_RAT_TYPE:
		case GTPIE_USER_LOC:
		case GTPIE_MS_TZ:
		case GTPIE_IMEI_SV:
		case GTPIE_CML_CHG_I_CT:
		case GTPIE_MBMS_UE_CTX:
		case GTPIE_TMGI:
		case GTPIE_RIM_ROUT_ADDR:
		case GTPIE_MBMS_PCO:
		case GTPIE_MBMS_SA:
		case GTPIE_SRNC_PDCP_CTX:
		case GTPIE_ADDL_TRACE:
		case GTPIE_HOP_CTR:
		case GTPIE_SEL_PLMN_ID:
		case GTPIE_MBMS_SESS_ID:
		case GTPIE_MBMS_2_3G_IND:
		case GTPIE_ENH_NSAPI:
		case GTPIE_MBMS_SESS_DUR:
		case GTPIE_A_MBMS_TRAC_I:
		case GTPIE_MBMS_S_REP_N:
		case GTPIE_MBMS_TTDT:
		case GTPIE_PS_HO_REQ_CTX:
		case GTPIE_BSS_CONTAINER:
		case GTPIE_CELL_ID:
		case GTPIE_PDU_NUMBERS:
		case GTPIE_BSSGP_CAUSE:
		case GTPIE_RQD_MBMS_BCAP:
		case GTPIE_RIM_RA_DISCR:
		case GTPIE_L_SETUP_PFCS:
		case GTPIE_PS_HO_XID_PAR:
		case GTPIE_MS_CHG_REP_A:
		case GTPIE_DIR_TUN_FLAGS:
		case GTPIE_CORREL_ID:
		case GTPIE_MBMS_FLOWI:
		case GTPIE_MBMS_MC_DIST:
		case GTPIE_MBMS_DIST_ACK:
		case GTPIE_R_IRAT_HO_INF:
		case GTPIE_RFSP_IDX:
		case GTPIE_FQDN:
		case GTPIE_E_ALL_PRIO_1:
		case GTPIE_E_ALL_PRIO_2:
		case GTPIE_E_CMN_FLAGS:
		case GTPIE_U_CSG_INFO:
		case GTPIE_CSG_I_REP_ACT:
		case GTPIE_CSG_ID:
		case GTPIE_CSG_MEMB_IND:
		case GTPIE_AMBR:
		case GTPIE_UE_NET_CAPA:
		case GTPIE_UE_AMBR:
		case GTPIE_APN_AMBR_NS:
		case GTPIE_GGSN_BACKOFF:
		case GTPIE_S_PRIO_IND:
		case GTPIE_S_PRIO_IND_NS:
		case GTPIE_H_BR_16MBPS_F:
		case GTPIE_A_MMCTX_SRVCC:
		case GTPIE_A_FLAGS_SRVCC:
		case GTPIE_STN_SR:
		case GTPIE_C_MSISDN:
		case GTPIE_E_RANAP_CAUSE:
		case GTPIE_ENODEB_ID:
		case GTPIE_SEL_MODE_NS:
		case GTPIE_ULI_TIMESTAMP:
		case GTPIE_CHARGING_ADDR:
		case GTPIE_PRIVATE:
			if (j < GTPIE_SIZE) {
				ie[j] = (union gtpie_member *)p;
				if (GTPIE_DEBUG)
					printf("GTPIE TLV found. Type %d\n",
					       ie[j]->tlv.t);
				p += 3 + ntoh16(ie[j]->tlv.l);
				j++;
			}
			break;
		default:
			if (GTPIE_DEBUG)
				printf("GTPIE something unknown. Type %d\n",
				       *p);
			return EOF;	/* We received something unknown */
		}
	}
	if (p == end) {
		if (GTPIE_DEBUG)
			printf("GTPIE normal return. %lx %lx\n",
			       (unsigned long)p, (unsigned long)end);
		return 0;	/* We landed at the end of the packet: OK */
	} else if (!(j < GTPIE_SIZE)) {
		if (GTPIE_DEBUG)
			printf("GTPIE too many elements.\n");
		return EOF;	/* We received too many information elements */
	} else {
		if (GTPIE_DEBUG)
			printf("GTPIE exceeded end of packet. %lx %lx\n",
			       (unsigned long)p, (unsigned long)end);
		return EOF;	/* We exceeded the end of the packet: Error */
	}
}

/*! Encode GTP packet payload from Array of Information Elements.
 *  \param[out] ie Input Array of GTPIE
 *  \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
 *  \param[out] len Encoded length of \a pack in bytes
 *  \returns 0 on sucess; 2 for out-of-space */
int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
{
	int i;
	unsigned char *p;
	unsigned char *end;
	int iesize;

	p = pack;

	memset(pack, 0, GTPIE_MAX);
	end = p + GTPIE_MAX;
	for (i = 1; i < GTPIE_SIZE; i++)
		if (ie[i] != 0) {
			if (GTPIE_DEBUG)
				printf("gtpie_encaps. Type %d\n", i);
			switch (i) {
			case GTPIE_CAUSE:	/* TV GTPIE types with value length 1 */
			case GTPIE_REORDER:
			case GTPIE_MAP_CAUSE:
			case GTPIE_MS_VALIDATED:
			case GTPIE_RECOVERY:
			case GTPIE_SELECTION_MODE:
			case GTPIE_TEARDOWN:
			case GTPIE_NSAPI:
			case GTPIE_RANAP_CAUSE:
			case GTPIE_RP_SMS:
			case GTPIE_RP:
			case GTPIE_MS_NOT_REACH:
			case GTPIE_BCM:
				iesize = 2;
				break;
			case GTPIE_FL_DI:	/* TV GTPIE types with value length 2 */
			case GTPIE_FL_C:
			case GTPIE_PFI:
			case GTPIE_CHARGING_C:
			case GTPIE_TRACE_REF:
			case GTPIE_TRACE_TYPE:
				iesize = 3;
				break;
			case GTPIE_QOS_PROFILE0:	/* TV GTPIE types with value length 3 */
			case GTPIE_P_TMSI_S:
				iesize = 4;
				break;
			case GTPIE_TLLI:	/* TV GTPIE types with value length 4 */
			case GTPIE_P_TMSI:
				/* case GTPIE_TEI_DI: only in gtp1 */
				/* case GTPIE_TEI_C: only in gtp1 */
			case GTPIE_CHARGING_ID:
				iesize = 5;
				break;
			case GTPIE_TEI_DII:	/* TV GTPIE types with value length 5 */
				iesize = 6;
				break;
			case GTPIE_RAI:		/* TV GTPIE types with value length 6 */
				iesize = 7;
				break;
			case GTPIE_RAB_CONTEXT:	/* TV GTPIE types with value length 7 */
				iesize = 8;
				break;
			case GTPIE_IMSI:	/* TV GTPIE types with value length 8 */
				iesize = 9;
				break;
			case GTPIE_AUTH_TRIPLET:	/* TV GTPIE types with value length 28 */
				iesize = 29;
				break;
			case GTPIE_EXT_HEADER_T:	/* GTP extension header */
				iesize = 2 + hton8(ie[i]->ext.l);
				break;
			case GTPIE_EUA:	/* TLV GTPIE types with length length 2 */
			case GTPIE_MM_CONTEXT:
			case GTPIE_PDP_CONTEXT:
			case GTPIE_APN:
			case GTPIE_PCO:
			case GTPIE_GSN_ADDR:
			case GTPIE_MSISDN:
			case GTPIE_QOS_PROFILE:
			case GTPIE_AUTH_QUINTUP:
			case GTPIE_TFT:
			case GTPIE_TARGET_INF:
			case GTPIE_UTRAN_TRANS:
			case GTPIE_RAB_SETUP:
			case GTPIE_TRIGGER_ID:
			case GTPIE_OMC_ID:
			case GTPIE_RAN_T_CONTAIN:
			case GTPIE_PDP_CTX_PRIO:
			case GTPIE_ADDL_RAB_S_I:
			case GTPIE_SGSN_NUMBER:
			case GTPIE_COMMON_FLAGS:
			case GTPIE_APN_RESTR:
			case GTPIE_R_PRIO_LCS:
			case GTPIE_RAT_TYPE:
			case GTPIE_USER_LOC:
			case GTPIE_MS_TZ:
			case GTPIE_IMEI_SV:
			case GTPIE_CML_CHG_I_CT:
			case GTPIE_MBMS_UE_CTX:
			case GTPIE_TMGI:
			case GTPIE_RIM_ROUT_ADDR:
			case GTPIE_MBMS_PCO:
			case GTPIE_MBMS_SA:
			case GTPIE_SRNC_PDCP_CTX:
			case GTPIE_ADDL_TRACE:
			case GTPIE_HOP_CTR:
			case GTPIE_SEL_PLMN_ID:
			case GTPIE_MBMS_SESS_ID:
			case GTPIE_MBMS_2_3G_IND:
			case GTPIE_ENH_NSAPI:
			case GTPIE_MBMS_SESS_DUR:
			case GTPIE_A_MBMS_TRAC_I:
			case GTPIE_MBMS_S_REP_N:
			case GTPIE_MBMS_TTDT:
			case GTPIE_PS_HO_REQ_CTX:
			case GTPIE_BSS_CONTAINER:
			case GTPIE_CELL_ID:
			case GTPIE_PDU_NUMBERS:
			case GTPIE_BSSGP_CAUSE:
			case GTPIE_RQD_MBMS_BCAP:
			case GTPIE_RIM_RA_DISCR:
			case GTPIE_L_SETUP_PFCS:
			case GTPIE_PS_HO_XID_PAR:
			case GTPIE_MS_CHG_REP_A:
			case GTPIE_DIR_TUN_FLAGS:
			case GTPIE_CORREL_ID:
			case GTPIE_MBMS_FLOWI:
			case GTPIE_MBMS_MC_DIST:
			case GTPIE_MBMS_DIST_ACK:
			case GTPIE_R_IRAT_HO_INF:
			case GTPIE_RFSP_IDX:
			case GTPIE_FQDN:
			case GTPIE_E_ALL_PRIO_1:
			case GTPIE_E_ALL_PRIO_2:
			case GTPIE_E_CMN_FLAGS:
			case GTPIE_U_CSG_INFO:
			case GTPIE_CSG_I_REP_ACT:
			case GTPIE_CSG_ID:
			case GTPIE_CSG_MEMB_IND:
			case GTPIE_AMBR:
			case GTPIE_UE_NET_CAPA:
			case GTPIE_UE_AMBR:
			case GTPIE_APN_AMBR_NS:
			case GTPIE_GGSN_BACKOFF:
			case GTPIE_S_PRIO_IND:
			case GTPIE_S_PRIO_IND_NS:
			case GTPIE_H_BR_16MBPS_F:
			case GTPIE_A_MMCTX_SRVCC:
			case GTPIE_A_FLAGS_SRVCC:
			case GTPIE_STN_SR:
			case GTPIE_C_MSISDN:
			case GTPIE_E_RANAP_CAUSE:
			case GTPIE_ENODEB_ID:
			case GTPIE_SEL_MODE_NS:
			case GTPIE_ULI_TIMESTAMP:
			case GTPIE_CHARGING_ADDR:
			case GTPIE_PRIVATE:
				iesize = 3 + hton16(ie[i]->tlv.l);
				break;
			default:
				return 2;	/* We received something unknown */
			}
			if (p + iesize < end) {
				memcpy(p, ie[i], iesize);
				p += iesize;
				*len += iesize;
			} else
				return 2;	/* Out of space */
		}
	return 0;
}

/*! Encode GTP packet payload from Array of Information Elements.
 *  \param[out] ie Input Array of GTPIE
 *  \param[in] size Size of ie
 *  \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
 *  \param[out] len Encoded length of \a pack in bytes
 *  \returns 0 on sucess; 2 for out-of-space */
int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
		  void *pack, unsigned *len)
{
	unsigned int i, j;
	unsigned char *p;
	unsigned char *end;
	int iesize;

	p = pack;

	memset(pack, 0, GTPIE_MAX);
	end = p + GTPIE_MAX;
	for (j = 0; j < GTPIE_SIZE; j++)
		for (i = 0; i < size; i++)
			if (ie[i].t == j) {
				if (GTPIE_DEBUG)
					printf
					    ("gtpie_encaps. Number %d, Type %d\n",
					     i, ie[i].t);
				switch (ie[i].t) {
				case GTPIE_CAUSE:	/* TV GTPIE types with value length 1 */
				case GTPIE_REORDER:
				case GTPIE_MAP_CAUSE:
				case GTPIE_MS_VALIDATED:
				case GTPIE_RECOVERY:
				case GTPIE_SELECTION_MODE:
				case GTPIE_TEARDOWN:
				case GTPIE_NSAPI:
				case GTPIE_RANAP_CAUSE:
				case GTPIE_RP_SMS:
				case GTPIE_RP:
				case GTPIE_MS_NOT_REACH:
				case GTPIE_BCM:
					iesize = 2;
					break;
				case GTPIE_PFI:	/* TV GTPIE types with value length 2 */
				case GTPIE_CHARGING_C:
				case GTPIE_TRACE_REF:
				case GTPIE_TRACE_TYPE:
					iesize = 3;
					break;
				case GTPIE_QOS_PROFILE0:	/* TV GTPIE types with value length 3 */
				case GTPIE_P_TMSI_S:
					iesize = 4;
					break;
				case GTPIE_TLLI:	/* TV GTPIE types with value length 4 */
				case GTPIE_P_TMSI:
				case GTPIE_TEI_DI:
				case GTPIE_TEI_C:
				case GTPIE_CHARGING_ID:
					iesize = 5;
					break;
				case GTPIE_TEI_DII:	/* TV GTPIE types with value length 5 */
					iesize = 6;
					break;
				case GTPIE_RAI:		/* TV GTPIE types with value length 6 */
					iesize = 7;
					break;
				case GTPIE_RAB_CONTEXT:	/* TV GTPIE types with value length 7 */
					iesize = 8;
					break;
				case GTPIE_IMSI:	/* TV GTPIE types with value length 8 */
					iesize = 9;
					break;
				case GTPIE_AUTH_TRIPLET:	/* TV GTPIE types with value length 28 */
					iesize = 29;
					break;
				case GTPIE_EXT_HEADER_T:	/* GTP extension header */
					iesize = 2 + hton8(ie[i].ext.l);
					break;
				case GTPIE_EUA:	/* TLV GTPIE types with length length 2 */
				case GTPIE_MM_CONTEXT:
				case GTPIE_PDP_CONTEXT:
				case GTPIE_APN:
				case GTPIE_PCO:
				case GTPIE_GSN_ADDR:
				case GTPIE_MSISDN:
				case GTPIE_QOS_PROFILE:
				case GTPIE_AUTH_QUINTUP:
				case GTPIE_TFT:
				case GTPIE_TARGET_INF:
				case GTPIE_UTRAN_TRANS:
				case GTPIE_RAB_SETUP:
				case GTPIE_TRIGGER_ID:
				case GTPIE_OMC_ID:
				case GTPIE_RAN_T_CONTAIN:
				case GTPIE_PDP_CTX_PRIO:
				case GTPIE_ADDL_RAB_S_I:
				case GTPIE_SGSN_NUMBER:
				case GTPIE_COMMON_FLAGS:
				case GTPIE_APN_RESTR:
				case GTPIE_R_PRIO_LCS:
				case GTPIE_RAT_TYPE:
				case GTPIE_USER_LOC:
				case GTPIE_MS_TZ:
				case GTPIE_IMEI_SV:
				case GTPIE_CML_CHG_I_CT:
				case GTPIE_MBMS_UE_CTX:
				case GTPIE_TMGI:
				case GTPIE_RIM_ROUT_ADDR:
				case GTPIE_MBMS_PCO:
				case GTPIE_MBMS_SA:
				case GTPIE_SRNC_PDCP_CTX:
				case GTPIE_ADDL_TRACE:
				case GTPIE_HOP_CTR:
				case GTPIE_SEL_PLMN_ID:
				case GTPIE_MBMS_SESS_ID:
				case GTPIE_MBMS_2_3G_IND:
				case GTPIE_ENH_NSAPI:
				case GTPIE_MBMS_SESS_DUR:
				case GTPIE_A_MBMS_TRAC_I:
				case GTPIE_MBMS_S_REP_N:
				case GTPIE_MBMS_TTDT:
				case GTPIE_PS_HO_REQ_CTX:
				case GTPIE_BSS_CONTAINER:
				case GTPIE_CELL_ID:
				case GTPIE_PDU_NUMBERS:
				case GTPIE_BSSGP_CAUSE:
				case GTPIE_RQD_MBMS_BCAP:
				case GTPIE_RIM_RA_DISCR:
				case GTPIE_L_SETUP_PFCS:
				case GTPIE_PS_HO_XID_PAR:
				case GTPIE_MS_CHG_REP_A:
				case GTPIE_DIR_TUN_FLAGS:
				case GTPIE_CORREL_ID:
				case GTPIE_MBMS_FLOWI:
				case GTPIE_MBMS_MC_DIST:
				case GTPIE_MBMS_DIST_ACK:
				case GTPIE_R_IRAT_HO_INF:
				case GTPIE_RFSP_IDX:
				case GTPIE_FQDN:
				case GTPIE_E_ALL_PRIO_1:
				case GTPIE_E_ALL_PRIO_2:
				case GTPIE_E_CMN_FLAGS:
				case GTPIE_U_CSG_INFO:
				case GTPIE_CSG_I_REP_ACT:
				case GTPIE_CSG_ID:
				case GTPIE_CSG_MEMB_IND:
				case GTPIE_AMBR:
				case GTPIE_UE_NET_CAPA:
				case GTPIE_UE_AMBR:
				case GTPIE_APN_AMBR_NS:
				case GTPIE_GGSN_BACKOFF:
				case GTPIE_S_PRIO_IND:
				case GTPIE_S_PRIO_IND_NS:
				case GTPIE_H_BR_16MBPS_F:
				case GTPIE_A_MMCTX_SRVCC:
				case GTPIE_A_FLAGS_SRVCC:
				case GTPIE_STN_SR:
				case GTPIE_C_MSISDN:
				case GTPIE_E_RANAP_CAUSE:
				case GTPIE_ENODEB_ID:
				case GTPIE_SEL_MODE_NS:
				case GTPIE_ULI_TIMESTAMP:
				case GTPIE_CHARGING_ADDR:
				case GTPIE_PRIVATE:
					iesize = 3 + hton16(ie[i].tlv.l);
					break;
				default:
					return 2;	/* We received something unknown */
				}
				if (p + iesize < end) {
					memcpy(p, &ie[i], iesize);
					p += iesize;
					*len += iesize;
				} else
					return 2;	/* Out of space */
			}
	return 0;
}

/*! Encode GTP packet payload from Array of Information Elements.
 *  \param[in] ie Input Array of GTPIE
 *  \param[in] ie_len Length of \a ie array
 *  \param[in] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
 *  \param[in] pack_len Length of \a pack buffer
 *  \param[out] encoded_len Encoded length of \a pack in bytes
 *  \returns 0 on success; 2 for out-of-space
 *  GTP requires a certain order, the call must follow those which are defined for every message */
int gtpie_encaps3(union gtpie_member *ies[], unsigned int ie_len,
		  void *pack, unsigned pack_len, unsigned *encoded_len)
{
	unsigned int i;
	unsigned char *p;
	unsigned char *end;
	int iesize;
	union gtpie_member *ie;

	*encoded_len = 0;
	p = pack;

	memset(pack, 0, pack_len);
	end = p + pack_len;
	for (i = 0; i < ie_len; i++) {
		if (!ies[i])
			continue;
		ie = ies[i];

		if (GTPIE_DEBUG)
			printf
			    ("gtpie_encaps. Number %d, Type %d\n",
			     i, ie->t);
		switch (ie->t) {
		case GTPIE_CAUSE:	/* TV GTPIE types with value length 1 */
		case GTPIE_REORDER:
		case GTPIE_MAP_CAUSE:
		case GTPIE_MS_VALIDATED:
		case GTPIE_RECOVERY:
		case GTPIE_SELECTION_MODE:
		case GTPIE_TEARDOWN:
		case GTPIE_NSAPI:
		case GTPIE_RANAP_CAUSE:
		case GTPIE_RP_SMS:
		case GTPIE_RP:
		case GTPIE_MS_NOT_REACH:
		case GTPIE_BCM:
			iesize = 2;
			break;
		case GTPIE_PFI:	/* TV GTPIE types with value length 2 */
		case GTPIE_CHARGING_C:
		case GTPIE_TRACE_REF:
		case GTPIE_TRACE_TYPE:
			iesize = 3;
			break;
		case GTPIE_QOS_PROFILE0:	/* TV GTPIE types with value length 3 */
		case GTPIE_P_TMSI_S:
			iesize = 4;
			break;
		case GTPIE_TLLI:	/* TV GTPIE types with value length 4 */
		case GTPIE_P_TMSI:
		case GTPIE_TEI_DI:
		case GTPIE_TEI_C:
		case GTPIE_CHARGING_ID:
			iesize = 5;
			break;
		case GTPIE_TEI_DII:	/* TV GTPIE types with value length 5 */
			iesize = 6;
			break;
		case GTPIE_RAI:		/* TV GTPIE types with value length 6 */
			iesize = 7;
			break;
		case GTPIE_RAB_CONTEXT:	/* TV GTPIE types with value length 7 */
			iesize = 8;
			break;
		case GTPIE_IMSI:	/* TV GTPIE types with value length 8 */
			iesize = 9;
			break;
		case GTPIE_AUTH_TRIPLET:	/* TV GTPIE types with value length 28 */
			iesize = 29;
			break;
		case GTPIE_EXT_HEADER_T:	/* GTP extension header */
			iesize = 2 + hton8(ie->ext.l);
			break;
		case GTPIE_EUA:	/* TLV GTPIE types with length length 2 */
		case GTPIE_MM_CONTEXT:
		case GTPIE_PDP_CONTEXT:
		case GTPIE_APN:
		case GTPIE_PCO:
		case GTPIE_GSN_ADDR:
		case GTPIE_MSISDN:
		case GTPIE_QOS_PROFILE:
		case GTPIE_AUTH_QUINTUP:
		case GTPIE_TFT:
		case GTPIE_TARGET_INF:
		case GTPIE_UTRAN_TRANS:
		case GTPIE_RAB_SETUP:
		case GTPIE_TRIGGER_ID:
		case GTPIE_OMC_ID:
		case GTPIE_RAN_T_CONTAIN:
		case GTPIE_PDP_CTX_PRIO:
		case GTPIE_ADDL_RAB_S_I:
		case GTPIE_SGSN_NUMBER:
		case GTPIE_COMMON_FLAGS:
		case GTPIE_APN_RESTR:
		case GTPIE_R_PRIO_LCS:
		case GTPIE_RAT_TYPE:
		case GTPIE_USER_LOC:
		case GTPIE_MS_TZ:
		case GTPIE_IMEI_SV:
		case GTPIE_CML_CHG_I_CT:
		case GTPIE_MBMS_UE_CTX:
		case GTPIE_TMGI:
		case GTPIE_RIM_ROUT_ADDR:
		case GTPIE_MBMS_PCO:
		case GTPIE_MBMS_SA:
		case GTPIE_SRNC_PDCP_CTX:
		case GTPIE_ADDL_TRACE:
		case GTPIE_HOP_CTR:
		case GTPIE_SEL_PLMN_ID:
		case GTPIE_MBMS_SESS_ID:
		case GTPIE_MBMS_2_3G_IND:
		case GTPIE_ENH_NSAPI:
		case GTPIE_MBMS_SESS_DUR:
		case GTPIE_A_MBMS_TRAC_I:
		case GTPIE_MBMS_S_REP_N:
		case GTPIE_MBMS_TTDT:
		case GTPIE_PS_HO_REQ_CTX:
		case GTPIE_BSS_CONTAINER:
		case GTPIE_CELL_ID:
		case GTPIE_PDU_NUMBERS:
		case GTPIE_BSSGP_CAUSE:
		case GTPIE_RQD_MBMS_BCAP:
		case GTPIE_RIM_RA_DISCR:
		case GTPIE_L_SETUP_PFCS:
		case GTPIE_PS_HO_XID_PAR:
		case GTPIE_MS_CHG_REP_A:
		case GTPIE_DIR_TUN_FLAGS:
		case GTPIE_CORREL_ID:
		case GTPIE_MBMS_FLOWI:
		case GTPIE_MBMS_MC_DIST:
		case GTPIE_MBMS_DIST_ACK:
		case GTPIE_R_IRAT_HO_INF:
		case GTPIE_RFSP_IDX:
		case GTPIE_FQDN:
		case GTPIE_E_ALL_PRIO_1:
		case GTPIE_E_ALL_PRIO_2:
		case GTPIE_E_CMN_FLAGS:
		case GTPIE_U_CSG_INFO:
		case GTPIE_CSG_I_REP_ACT:
		case GTPIE_CSG_ID:
		case GTPIE_CSG_MEMB_IND:
		case GTPIE_AMBR:
		case GTPIE_UE_NET_CAPA:
		case GTPIE_UE_AMBR:
		case GTPIE_APN_AMBR_NS:
		case GTPIE_GGSN_BACKOFF:
		case GTPIE_S_PRIO_IND:
		case GTPIE_S_PRIO_IND_NS:
		case GTPIE_H_BR_16MBPS_F:
		case GTPIE_A_MMCTX_SRVCC:
		case GTPIE_A_FLAGS_SRVCC:
		case GTPIE_STN_SR:
		case GTPIE_C_MSISDN:
		case GTPIE_E_RANAP_CAUSE:
		case GTPIE_ENODEB_ID:
		case GTPIE_SEL_MODE_NS:
		case GTPIE_ULI_TIMESTAMP:
		case GTPIE_CHARGING_ADDR:
		case GTPIE_PRIVATE:
			iesize = 3 + hton16(ie->tlv.l);
			break;
		default:
			return 2;	/* We received something unknown */
		}

		if (p + iesize < end) {
			memcpy(p, ie, iesize);
			p += iesize;
			*encoded_len += iesize;
		} else
			return 2;	/* Out of space */
	}

	return 0;
}