[BACK]Return to print-gtp.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / tcpdump

File: [local] / src / usr.sbin / tcpdump / print-gtp.c (download)

Revision 1.13, Mon Oct 26 23:19:18 2020 UTC (3 years, 7 months ago) by jca
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, HEAD
Changes since 1.12: +6 -1 lines

Fix parsing GTP packets with invalid extended headers.

In GTP a zero extended header length is invalid, deal with this instead
of looping forever.

Report and fix by Peter J. Philipp, tweaked by me, ok kn@

/*	$OpenBSD: print-gtp.c,v 1.13 2020/10/26 23:19:18 jca Exp $ */
/*
 * Copyright (c) 2009, 2010 Joel Sing <jsing@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Decoder for the GPRS Trunking Protocol (GTP).
 *
 * This work has been kindly sponsored by SystemNet (www.systemnet.no).
 *
 * GTPv0 standards are available from the ETSI website:
 *
 *     http://pda.etsi.org/pda/
 *
 * GTPv1 standards are available from the 3GPP website:
 *
 *     http://www.3gpp.org/specifications
 *
 * The following standards have been referenced to create this decoder:
 *
 *     ETSI GSM 09.60 - GPRS Tunnelling Protocol (GTPv0)
 *     ETSI GSM 12.15 - GPRS Charging (GTPv0')
 *
 *     3GPP TS 23.003 - Numbering, addressing and identification
 *     3GPP TS 24.008 - Core network protocols
 *     3GPP TS 29.002 - Mobile Application Part (MAP) specification
 *     3GPP TS 29.060 - GPRS Tunnelling Protocol (GTPv1-C/GTPv1-U)
 *     3GPP TS 32.295 - Charging Data Record (CDR) transfer (GTPv1')
 */

#include <sys/time.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>

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

#include "addrtoname.h"
#include "interface.h"
#include "gtp.h"

#ifndef nitems
#define nitems(_a)  (sizeof((_a)) / sizeof((_a)[0]))
#endif

void	gtp_print(const u_char *, u_int, u_short, u_short);
void	gtp_decode_ie(const u_char *, u_short, int);
void	gtp_print_tbcd(const u_char *, u_int);
void	gtp_print_user_address(const u_char *, u_int);
void	gtp_print_apn(const u_char *, u_int);
void	gtp_print_str(const char **, u_int, u_int);

void	gtp_v0_print(const u_char *, u_int, u_short, u_short);
void	gtp_v0_print_prime(const u_char *);
int	gtp_v0_print_tv(const u_char *, u_int);
int	gtp_v0_print_tlv(const u_char *, u_int);

void	gtp_v1_print(const u_char *, u_int, u_short, u_short);
void	gtp_v1_print_ctrl(const u_char *, u_int, struct gtp_v1_hdr *);
void	gtp_v1_print_user(const u_char *, u_int, struct gtp_v1_hdr *);
void	gtp_v1_print_prime(const u_char *, struct gtp_v1_prime_hdr *);
int	gtp_v1_print_tv(const u_char *, u_int);
int	gtp_v1_print_tlv(const u_char *, u_int);

/* GTPv0 message types. */
static struct tok gtp_v0_msgtype[] = {

	{ 1,	"Echo Request" },
	{ 2,	"Echo Response" },
	{ 3,	"Version Not Supported" },
	{ 4,	"Node Alive Request" },
	{ 5,	"Node Alive Response" },
	{ 6,	"Redirection Request" },
	{ 7,	"Redirection Response" },
	{ 16,	"Create PDP Context Request" },
	{ 17,	"Create PDP Context Response" },
	{ 18,	"Update PDP Context Request" },
	{ 19,	"Update PDP Context Response" },
	{ 20,	"Delete PDP Context Request" },
	{ 21,	"Delete PDP Context Response" },
	{ 22,	"Create AA PDP Context Request" },
	{ 23,	"Create AA PDP Context Response" },
	{ 24,	"Delete AA PDP Context Request" },
	{ 25,	"Delete AA PDP Context Response" },
	{ 26,	"Error Indication" },
	{ 27,	"PDU Notification Request" },
	{ 28,	"PDU Notification Response" },
	{ 29,	"PDU Notification Reject Request" },
	{ 30,	"PDU Notification Reject Response" },
	{ 32,	"Send Routeing Information Request" },
	{ 33,	"Send Routeing Information Response" },
	{ 34,	"Failure Report Request" },
	{ 35,	"Failure Report Response" },
	{ 36,	"MS GPRS Present Request" },
	{ 37,	"MS GPRS Present Response" },
	{ 48,	"Identification Request" },
	{ 49,	"Identification Response" },
	{ 50,	"SGSN Context Request" },
	{ 51,	"SGSN Context Response" },
	{ 52,	"SGSN Context Acknowledge" },
	{ 240,	"Data Record Transfer Request" },
	{ 241,	"Data Record Transfer Response" },
	{ 255,	"T-PDU" },

	{ 0,	NULL }
};

/* GTPv0 causes. */
static struct tok gtp_v0_cause[] = {

	{ 0,	"Request IMSI" },
	{ 1,	"Request IMEI" },
	{ 2,	"Request IMSI and IMEI" },
	{ 3,	"No identity needed" },
	{ 4,	"MS refuses" },
	{ 5,	"MS is not GPRS responding" },
	{ 128,	"Request accepted" },
	{ 192,	"Non-existent" },
	{ 193,	"Invalid message format" },
	{ 194,	"IMSI not known" },
	{ 195,	"MS is GPRS detached" },
	{ 196,	"MS is not GPRS responding" },
	{ 197,	"MS refuses" },
	{ 198,	"Version not supported" },
	{ 199,	"No resources available" },
	{ 200,	"Service not supported" },
	{ 201,	"Mandatory IE incorrect" },
	{ 202,	"Mandatory IE missing" },
	{ 203,	"Optional IE incorrect" },
	{ 204,	"System failure" },
	{ 205,	"Roaming restriction" },
	{ 206,	"P-TMSI signature mismatch" },
	{ 207,	"GPRS connection suspended" },
	{ 208,	"Authentication failure" },
	{ 209,	"User authentication failed" },

	{ 0,	NULL }
};

/* GTPv1 message types. */
static struct tok gtp_v1_msgtype[] = {

	{ 1,	"Echo Request" },
	{ 2,	"Echo Response" },
	{ 3,	"Version Not Supported" },
	{ 4,	"Node Alive Request" },
	{ 5,	"Node Alive Response" },
	{ 6,	"Redirection Request" },
	{ 7,	"Redirection Response" },
	{ 16,	"Create PDP Context Request" },
	{ 17,	"Create PDP Context Response" },
	{ 18,	"Update PDP Context Request" },
	{ 19,	"Update PDP Context Response" },
	{ 20,	"Delete PDP Context Request" },
	{ 21,	"Delete PDP Context Response" },
	{ 22,	"Initiate PDP Context Activiation Request" },
	{ 23,	"Initiate PDP Context Activiation Response" },
	{ 26,	"Error Indication" },
	{ 27,	"PDU Notification Request" },
	{ 28,	"PDU Notification Response" },
	{ 29,	"PDU Notification Reject Request" },
	{ 30,	"PDU Notification Reject Response" },
	{ 31,	"Supported Extension Headers Notification" },
	{ 32,	"Send Routeing Information for GPRS Request" },
	{ 33,	"Send Routeing Information for GPRS Response" },
	{ 34,	"Failure Report Request" },
	{ 35,	"Failure Report Response" },
	{ 36,	"Note MS GPRS Present Request" },
	{ 37,	"Note MS GPRS Present Response" },
	{ 48,	"Identification Request" },
	{ 49,	"Identification Response" },
	{ 50,	"SGSN Context Request" },
	{ 51,	"SGSN Context Response" },
	{ 52,	"SGSN Context Acknowledge" },
	{ 53,	"Forward Relocation Request" },
	{ 54,	"Forward Relocation Response" },
	{ 55,	"Forward Relocation Complete" },
	{ 56,	"Relocation Cancel Request" },
	{ 57,	"Relocation Cancel Response" },
	{ 58,	"Forward SRNS Context" },
	{ 59,	"Forward Relocation Complete Acknowledge" },
	{ 60,	"Forward SRNS Context Acknowledge" },
	{ 70,	"RAN Information Relay" },
	{ 96,	"MBMS Notification Request" },
	{ 97,	"MBMS Notification Response" },
	{ 98,	"MBMS Notification Reject Request" },
	{ 99,	"MBMS Notification Reject Response" },
	{ 100,	"Create MBMS Context Request" },
	{ 101,	"Create MBMS Context Response" },
	{ 102,	"Update MBMS Context Request" },
	{ 103,	"Update MBMS Context Response" },
	{ 104,	"Delete MBMS Context Request" },
	{ 105,	"Delete MBMS Context Response" },
	{ 112,	"MBMS Registration Request" },
	{ 113,	"MBMS Registration Response" },
	{ 114,	"MBMS De-Registration Request" },
	{ 115,	"MBMS De-Registration Response" },
	{ 116,	"MBMS Session Start Request" },
	{ 117,	"MBMS Session Start Response" },
	{ 118,	"MBMS Session Stop Request" },
	{ 119,	"MBMS Session Stop Response" },
	{ 120,	"MBMS Session Update Request" },
	{ 121,	"MBMS Session Update Response" },
	{ 128,	"MBMS Info Change Notification Request" },
	{ 129,	"MBMS Info Change Notification Response" },
	{ 240,	"Data Record Transfer Request" },
	{ 241,	"Data Record Transfer Response" },
	{ 255,	"G-PDU" },

	{ 0,	NULL }
};

/* GTPv1 Causes. */
static struct tok gtp_v1_cause[] = {

	/* GTPv1-C. */
	{ 0,	"Request IMSI" },
	{ 1,	"Request IMEI" },
	{ 2,	"Request IMSI and IMEI" },
	{ 3,	"No identity needed" },
	{ 4,	"MS refuses" },
	{ 5,	"MS is not GPRS responding" },
	{ 128,	"Request accepted" },
	{ 192,	"Non-existent" },
	{ 193,	"Invalid message format" },
	{ 194,	"IMSI not known" },
	{ 195,	"MS is GPRS detached" },
	{ 196,	"MS is not GPRS responding" },
	{ 197,	"MS refuses" },
	{ 198,	"Version not supported" },
	{ 199,	"No resources available" },
	{ 200,	"Service not supported" },
	{ 201,	"Mandatory IE incorrect" },
	{ 202,	"Mandatory IE missing" },
	{ 203,	"Optional IE incorrect" },
	{ 204,	"System failure" },
	{ 205,	"Roaming restriction" },
	{ 206,	"P-TMSI signature mismatch" },
	{ 207,	"GPRS connection suspended" },
	{ 208,	"Authentication failure" },
	{ 209,	"User authentication failed" },
	{ 210,	"Context not found" },
	{ 211,	"All dynamic PDP addresses are occupied" },
	{ 212,	"No memory is available" },
	{ 213,	"Relocation failure" },
	{ 214,	"Unknown mandatory extension header" },
	{ 215,	"Semantic error in the TFT operation" },
	{ 216,	"Syntactic error in the TFT operation" },
	{ 217,	"Semantic errors in packet filter(s)" },
	{ 218,	"Syntactic errors in packet filter(s)" },
	{ 219,	"Missing or unknown APN" },
	{ 220,	"Unknown PDP address or PDP type" },
	{ 221,	"PDP context without TFT already activated" },
	{ 222,	"APN access denied - no subscription" },
	{ 223,	"APN restriction type incompatibility with currently "
		"active PDP contexts" },
	{ 224,	"MS MBMS capabilities insufficient" },
	{ 225,	"Invalid correlation-ID" },
	{ 226,	"MBMS bearer context superseded" },

	/* GTP'v1. */
	{ 59,	"System failure" },
	{ 60,	"The transmit buffers are becoming full" },
	{ 61,	"The receive buffers are becoming full" },
	{ 62,	"Another node is about to go down" },
	{ 63,	"This node is about to go down" },
	{ 177,	"CDR decoding error" },
	{ 252,	"Request related to possibly duplicated packets already "
		"fulfilled" },
	{ 253,	"Request already fulfilled" },
	{ 254,	"Sequence numbers of released/cancelled packets IE incorrect" },
	{ 255,	"Request not fulfilled" },

	{ 0,	NULL }
};

static int gtp_proto = -1;

void
gtp_print(const u_char *cp, u_int length, u_short sport, u_short dport)
{
	int version;

	/* Decode GTP version. */
	TCHECK(cp[0]);
	version = cp[0] >> GTP_VERSION_SHIFT;

	if (version == GTP_VERSION_0)
		gtp_v0_print(cp, length, sport, dport);
	else if (version == GTP_VERSION_1)
		gtp_v1_print(cp, length, sport, dport);
	else
		printf("GTP (version %i)", version);

	return;

trunc:
	printf("[|GTP]");
}

/*
 * Decode and print information elements from message. The actual work is
 * handled in the appropriate Tag/Value (TV) or Tag/Length/Value (TLV)
 * decoding routine.
 */
void
gtp_decode_ie(const u_char *cp, u_short version, int len)
{
	int val, ielen, iecount = 0;

	if (len <= 0)
		return;

	printf(" {");

	while (len > 0) {

		iecount++;
		if (iecount > 1)
			printf(" ");

		TCHECK(cp[0]);
		val = (u_int)cp[0];
		cp++;

		printf("[");

		switch (version) {
		case GTP_VERSION_0:
			if ((val & GTPV0_IE_TYPE_MASK) == 0)
				ielen = gtp_v0_print_tv(cp, val);
			else
				ielen = gtp_v0_print_tlv(cp, val);
			break;

		case GTP_VERSION_1:
			if ((val & GTPV1_IE_TYPE_MASK) == 0)
				ielen = gtp_v1_print_tv(cp, val);
			else
				ielen = gtp_v1_print_tlv(cp, val);
			break;

		default:
			/* Version not supported... */
			ielen = -1;
			break;
		}

		printf("]");

		if (ielen < 0)
			goto trunc;

		len -= ielen;
		cp += ielen - 1;
	}

	if (iecount > 0)
		printf("}");

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

/*
 * Decode and print telephony binary coded decimal.
 */
void
gtp_print_tbcd(const u_char *cp, u_int len)
{
	u_int8_t *data, bcd;
	int i;

	data = (u_int8_t *)cp;
	for (i = 0; i < len; i++) {
		bcd = *data & 0xf;
		if (bcd != 0xf)
			printf("%u", bcd);
		bcd = *data >> 4;
		if (bcd != 0xf)
			printf("%u", bcd);
		data++;
	}
}

/*
 * Decode and print an end user address. Format is detailed in
 * GSM 09.60 section 7.9.18 and 3GPP 29.060 section 7.7.27.
 */
void
gtp_print_user_address(const u_char *cp, u_int len)
{
	u_int8_t org, type;

	if (len < 2)
		return;

	org = (u_int8_t)cp[0] & 0xf;
	type = (u_int8_t)cp[1];

	cp += 2;

	if (org == 0x0 && type == 0x1)
		printf(": PPP");
	else if (org == 0x1 && type == 0x21) {
		if (len == 6)
			printf(": %s", ipaddr_string(cp));
		else
			printf(": IPv4");
	} else if (org == 0x1 && type == 0x57) {
		if (len == 18)
			printf(": %s", ip6addr_string(cp));
		else
			printf(": IPv6");
	} else
		printf(" (org 0x%x, type 0x%x)", org, type);
}

/*
 * Decode and print an Access Point Name. Format is detailed in
 * 3GPP 24.008 section 10.5.6.1 and 3GPP 23.003 section 9.1.
 */
void
gtp_print_apn(const u_char *cp, u_int len)
{
	u_char label[100];
	u_int8_t llen;

	if (len < 1 || len > 100)
		return;

	while (len > 0) {

		llen = (u_int8_t)cp[0];
		if (llen > 99)
			return;

		bcopy(cp + 1, label, llen);
		label[llen] = '\0';
		printf("%s", label);

		cp += llen + 1;
		len -= llen + 1;

		if (len > 0)
			printf(".");

	}	
}

/* Print string from array. */
void
gtp_print_str(const char **strs, u_int bound, u_int index)
{
	if (index >= bound)
		printf(": %u", index);
	else if (strs[index] != NULL)
		printf(": %s", strs[index]);
}

/*
 * Decoding routines for GTP version 0.
 */
void
gtp_v0_print(const u_char *cp, u_int length, u_short sport, u_short dport)
{
	struct gtp_v0_hdr *gh = (struct gtp_v0_hdr *)cp;
	int len, version;
	u_int64_t tid;

	gtp_proto = GTP_V0_PROTO;

	/* Check if this is GTP prime. */
	TCHECK(gh->flags);
	if ((gh->flags & GTPV0_HDR_PROTO_TYPE) == 0) {
		gtp_proto = GTP_V0_PRIME_PROTO;
		gtp_v0_print_prime(cp);
		return;
	}

	/* Print GTP header. */
	TCHECK(*gh);
	cp += sizeof(struct gtp_v0_hdr);
	len = ntohs(gh->length);
	bcopy(&gh->tid, &tid, sizeof(tid));
	printf("GTPv0 (len %u, seqno %u, flow %u, N-PDU %u, tid 0x%llx) ",
	    ntohs(gh->length), ntohs(gh->seqno), ntohs(gh->flow),
	    ntohs(gh->npduno), betoh64(tid));

	/* Decode GTP message. */
	printf("%s", tok2str(gtp_v0_msgtype, "Message Type %u", gh->msgtype));

	if (!vflag)
		return;

	if (gh->msgtype == GTPV0_T_PDU) {

		TCHECK(cp[0]);
		version = cp[0] >> 4;

		printf(" { ");

		if (version == 4)
			ip_print(cp, len);
		else if (version == 6)
			ip6_print(cp, len);
		else
			printf("Unknown IP version %u", version);

		printf(" }");
	} else 
		gtp_decode_ie(cp, GTP_VERSION_0, len);

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

void
gtp_v0_print_prime(const u_char *cp)
{
	struct gtp_v0_prime_hdr *gph = (struct gtp_v0_prime_hdr *)cp;
	int len;
	
	/* Decode GTP prime header. */
	TCHECK(*gph);
	cp += sizeof(*gph);

	len = ntohs(gph->length);
	printf("GTPv0' (len %u, seq %u) ", len, ntohs(gph->seqno));

	/* Decode GTP message. */
	printf("%s", tok2str(gtp_v0_msgtype, "Message Type %u", gph->msgtype));

	if (vflag)
		gtp_decode_ie(cp, GTP_VERSION_0, len);

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

int
gtp_v0_print_tv(const u_char *cp, u_int value)
{
	u_int32_t *dpl;
	u_int16_t *dps;
	u_int8_t data;
	int ielen = -1;

	switch (value) {
	case GTPV0_TV_CAUSE:

		/* 09.60 7.9.1 - Cause. */
		TCHECK(cp[0]);
		data = (u_int8_t)cp[0];
		ielen = GTPV0_TV_CAUSE_LENGTH;
		printf("Cause: %s", tok2str(gtp_v0_cause, "#%u", data));
		break;

	case GTPV0_TV_IMSI:

		/* 09.60 7.9.2 - International Mobile Subscriber Identity. */
		TCHECK2(cp[0], GTPV0_TV_IMSI_LENGTH - 1);
		printf("IMSI ");
		gtp_print_tbcd(cp, GTPV0_TV_IMSI_LENGTH - 1);
		ielen = GTPV0_TV_IMSI_LENGTH;
		break;

	case GTPV0_TV_RAI:

		/* 09.60 7.9.3 - Routing Area Identity (RAI). */
		TCHECK2(cp[0], GTPV0_TV_RAI_LENGTH - 1);
		printf("RAI: MCC ");
		data = cp[1] | 0xf0;
		gtp_print_tbcd(cp, 1);
		gtp_print_tbcd(&data, 1);
		printf(", MNC ");
		data = (cp[1] >> 4) | 0xf0;
		gtp_print_tbcd(cp + 2, 1);
		gtp_print_tbcd(&data, 1);
		printf(", LAC 0x%x%x", cp[3], cp[4]);
		printf(", RAC 0x%x", cp[5]);
		ielen = GTPV0_TV_RAI_LENGTH;
		break;

	case GTPV0_TV_TLLI:

		/* 09.60 7.9.4 - Temporary Logical Link Identity (TLLI). */
		TCHECK2(cp[0], GTPV0_TV_TLLI_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("TLLI 0x%x", ntohl(*dpl));
		ielen = GTPV0_TV_TLLI_LENGTH;
		break;

	case GTPV0_TV_PTMSI:

		/* 09.60 7.9.5 - Packet TMSI (P-TMSI). */
		TCHECK2(cp[0], GTPV0_TV_PTMSI_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("P-TMSI 0x%x", ntohl(*dpl));
		ielen = GTPV0_TV_PTMSI_LENGTH;
		break;

	case GTPV0_TV_QOS:

		/* 09.60 7.9.6 - Quality of Service (QoS) Profile. */
		TCHECK2(cp[0], GTPV0_TV_QOS_LENGTH - 1);
		printf("QoS Profile");				/* XXX */
		ielen = GTPV0_TV_QOS_LENGTH;
		break;

	case GTPV0_TV_REORDER:

		/* 09.60 7.9.7 - Reordering Required. */
		TCHECK2(cp[0], GTPV0_TV_REORDER_LENGTH - 1);
		printf("Reordering Required: ");
		if (cp[0] & 0x1)
			printf("yes");
		else
			printf("no");
		ielen = GTPV0_TV_REORDER_LENGTH;
		break;

	case GTPV0_TV_AUTH_TRIPLET:

		/* 09.60 7.9.8 - Authentication Triplet. */
		TCHECK2(cp[0], GTPV0_TV_AUTH_TRIPLET_LENGTH - 1);
		printf("Authentication");			/* XXX */
		ielen = GTPV0_TV_AUTH_TRIPLET_LENGTH;
		break;

	case GTPV0_TV_MAP_CAUSE:

		/* 09.60 7.9.9 - MAP Cause. */
		TCHECK2(cp[0], GTPV0_TV_MAP_CAUSE_LENGTH - 1);
		printf("MAP Cause: %u", cp[0]);
		ielen = GTPV0_TV_MAP_CAUSE_LENGTH;
		break;

	case GTPV0_TV_PTMSI_SIGNATURE:

		/* 09.60 7.9.10 - P-TMSI Signature. */
		/* Signature defined in GSM 04.08. */
		TCHECK2(cp[0], GTPV0_TV_PTMSI_SIGNATURE_LENGTH - 1);
		printf("PTMSI Signature: 0x%x%x%x", cp[0], cp[1], cp[2]);
		ielen = GTPV0_TV_PTMSI_SIGNATURE_LENGTH;
		break;

	case GTPV0_TV_MS_VALIDATED:

		/* 09.60 7.9.11 - MS Validated. */
		TCHECK2(cp[0], GTPV0_TV_MS_VALIDATED_LENGTH - 1);
		printf("MS Validated");
		if (cp[0] & 0x1)
			printf("yes");
		else
			printf("no");
		ielen = GTPV0_TV_MS_VALIDATED_LENGTH;
		break;

	case GTPV0_TV_RECOVERY:

		/* 09.60 7.9.12 - Recovery. */
		TCHECK2(cp[0], GTPV0_TV_RECOVERY_LENGTH - 1);
		printf("Recovery: Restart counter %u", cp[0]);
		ielen = GTPV0_TV_RECOVERY_LENGTH;
		break;

	case GTPV0_TV_SELECTION_MODE:

		/* 09.60 7.9.13 - Selection Mode. */
		TCHECK2(cp[0], GTPV0_TV_SELECTION_MODE_LENGTH - 1);
		printf("Selection Mode");			/* XXX */
		ielen = GTPV0_TV_SELECTION_MODE_LENGTH;
		break;

	case GTPV0_TV_FLOW_LABEL_DATA_I:

		/* 09.60 7.9.14 - Flow Label Data I. */
		TCHECK2(cp[0], GTPV0_TV_FLOW_LABEL_DATA_I_LENGTH - 1);
		dps = (u_int16_t *)cp;
		printf("Flow Label Data I: %u", ntohs(*dps));
		ielen = GTPV0_TV_FLOW_LABEL_DATA_I_LENGTH;
		break;

	case GTPV0_TV_FLOW_LABEL_SIGNALLING:

		/* 09.60 7.9.15 - Flow Label Signalling. */
		TCHECK2(cp[0], GTPV0_TV_FLOW_LABEL_SIGNALLING_LENGTH - 1);
		dps = (u_int16_t *)cp;
		printf("Flow Label Signalling: %u", ntohs(*dps));
		ielen = GTPV0_TV_FLOW_LABEL_SIGNALLING_LENGTH;
		break;

	case GTPV0_TV_FLOW_LABEL_DATA_II:

		/* 09.60 7.9.16 - Flow Label Data II. */
		TCHECK2(cp[0], GTPV0_TV_FLOW_LABEL_DATA_II_LENGTH - 1);
		data = cp[0] & 0xf;
		dps = (u_int16_t *)(cp + 1);
		printf("Flow Label Data II: %u, NSAPI %u", ntohs(*dps), data);
		ielen = GTPV0_TV_FLOW_LABEL_DATA_II_LENGTH;
		break;

	case GTPV0_TV_PACKET_XFER_CMD:

		/* 12.15 7.3.4.5.3 - Packet Transfer Command. */
		TCHECK2(cp[0], GTPV0_TV_PACKET_XFER_CMD_LENGTH - 1);
		printf("Packet Transfer Command");
		gtp_print_str(gtp_packet_xfer_cmd, nitems(gtp_packet_xfer_cmd),
		    cp[0]);
		ielen = GTPV0_TV_PACKET_XFER_CMD_LENGTH;
		break;
		
	case GTPV0_TV_CHARGING_ID:

		/* 09.60 7.9.17 - Charging ID. */
		TCHECK2(cp[0], GTPV0_TV_CHARGING_ID_LENGTH - 1);
		dps = (u_int16_t *)cp;
		printf("Charging ID: %u", ntohs(*dps));
		ielen = GTPV0_TV_CHARGING_ID_LENGTH;
		break;

	default:
		printf("TV %u", value);
	}

trunc:
	return ielen;
}

int
gtp_v0_print_tlv(const u_char *cp, u_int value)
{
	u_int8_t data;
	u_int16_t *lenp, *seqno, len;
	int ielen = -1;

	/* Get length of IE. */
	TCHECK2(cp[0], 2);
	lenp = (u_int16_t *)cp;
	cp += 2;
	len = ntohs(*lenp);
	TCHECK2(cp[0], len);
	ielen = sizeof(data) + sizeof(len) + len;

	switch (value) {

	case GTPV0_TLV_END_USER_ADDRESS:

		/* 09.60 7.9.18 - End User Address. */
		printf("End User Address");
		gtp_print_user_address(cp, len);
		break;

	case GTPV0_TLV_MM_CONTEXT:

		/* 09.60 7.9.19 - MM Context. */
		printf("MM Context");				/* XXX */
		break;

	case GTPV0_TLV_PDP_CONTEXT:

		/* 09.60 7.9.20 - PDP Context. */
		printf("PDP Context");				/* XXX */
		break;

	case GTPV0_TLV_ACCESS_POINT_NAME:

		/* 09.60 7.9.21 - Access Point Name. */
		printf("AP Name: ");
		gtp_print_apn(cp, len);
		break;

	case GTPV0_TLV_PROTOCOL_CONFIG_OPTIONS:

		/* 09.60 7.9.22 - Protocol Configuration Options. */
		printf("Protocol Configuration Options");	/* XXX */
		break;

	case GTPV0_TLV_GSN_ADDRESS:

		/* 09.60 7.9.23 - GSN Address. */
		printf("GSN Address");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		else if (len == 16)
			printf(": %s", ip6addr_string(cp));
		break;

	case GTPV0_TLV_MS_ISDN:

		/* 09.60 7.9.24 - MS International PSTN/ISDN Number. */
		printf("MSISDN ");
		data = (u_int8_t)cp[0];		/* XXX - Number type. */
		gtp_print_tbcd(cp + 1, len - 1);
		break;

	case GTPV0_TLV_CHARGING_GATEWAY_ADDRESS:

		/* 09.60 7.9.25 - Charging Gateway Address. */
		printf("Charging Gateway");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		break;

	case GTPV0_TLV_DATA_RECORD_PACKET:

		/* 12.15 7.3.4.5.4 - Data Record Packet. */
		printf("Data Record: Records %u, Format %u, Format Version %u",
		    cp[0], cp[1], ntohs(*(u_int16_t *)(cp + 2)));
		break;

	case GTPV0_TLV_REQUESTS_RESPONDED:

		/* 12.15 7.3.4.6 - Requests Responded. */
		printf("Requests Responded:");
		seqno = (u_int16_t *)cp;
		while (len > 0) {
			printf(" %u", ntohs(*seqno));
			seqno++;
			len -= sizeof(*seqno);
		}
		break;

	case GTPV0_TLV_RECOMMENDED_NODE:

		/* 12.15 7.3.4.3 - Address of Recommended Node. */
		printf("Recommended Node");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		else if (len == 16)
			printf(": %s", ip6addr_string(cp));
		break;

	case GTPV0_TLV_PRIVATE_EXTENSION:

		printf("Private Extension");
		break;

	default:
		printf("TLV %u (len %u)", value, len);
	}

	return ielen;

trunc:
	return -1;
}

/*
 * Decoding for GTP version 1, which consists of GTPv1-C, GTPv1-U and GTPv1'.
 */
void
gtp_v1_print(const u_char *cp, u_int length, u_short sport, u_short dport)
{
	struct gtp_v1_hdr *gh = (struct gtp_v1_hdr *)cp;
	struct gtp_v1_hdr_ext *ghe = NULL;
	int nexthdr, hlen;
	u_char *p = (u_char *)cp;

	TCHECK(gh->flags);
	if ((gh->flags & GTPV1_HDR_PROTO_TYPE) == 0) {
		gtp_proto = GTP_V1_PRIME_PROTO;
		printf(" GTPv1'");
		gtp_v1_print_prime(p, (struct gtp_v1_prime_hdr *)gh);
		return;
	}

	if (dport == GTPV1_C_PORT || sport == GTPV1_C_PORT) {
		gtp_proto = GTP_V1_CTRL_PROTO;
		printf(" GTPv1-C");
	} else if (dport == GTPV1_U_PORT || sport == GTPV1_U_PORT) {
		gtp_proto = GTP_V1_USER_PROTO;
		printf(" GTPv1-U");
	} else if (dport == GTPV1_PRIME_PORT || sport == GTPV1_PRIME_PORT) {
		gtp_proto = GTP_V1_PRIME_PROTO;
		printf(" GTPv1'");
	}

	/* Decode GTP header. */
	TCHECK(*gh);
	p += sizeof(struct gtp_v1_hdr);

	printf(" (teid %u, len %u)", ntohl(gh->teid), ntohs(gh->length));

	if (gh->flags & GTPV1_HDR_EXT) {
		ghe = (struct gtp_v1_hdr_ext *)cp;
		TCHECK(*ghe);
		p += sizeof(struct gtp_v1_hdr_ext) - sizeof(struct gtp_v1_hdr);
	}

	if (gh->flags & GTPV1_HDR_SN_FLAG)
		printf(" [seq %u]", ntohs(ghe->seqno));

	if (gh->flags & GTPV1_HDR_NPDU_FLAG)
		printf(" [N-PDU %u]", ghe->npduno);

	if (gh->flags & GTPV1_HDR_EH_FLAG) {

		/* Process next header... */
		nexthdr = ghe->nexthdr;
		while (nexthdr != GTPV1_EH_NONE) {

			/* Header length is a 4 octet multiplier. */
			hlen = (int)p[0] * 4;
			if (hlen == 0) {
				printf(" [Invalid zero-length header %u]",
				    nexthdr);
				goto trunc;
			}
			TCHECK2(p[0], hlen);

			switch (nexthdr) {
			case GTPV1_EH_MBMS_SUPPORT:
				printf(" [MBMS Support]");
				break;

			case GTPV1_EH_MSI_CHANGE_RPT:
				printf(" [MS Info Change Reporting]");
				break;

			case GTPV1_EH_PDCP_PDU_NO:
				printf(" [PDCP PDU %u]",
				    ntohs(*(u_int16_t *)(p + 1)));
				break;

			case GTPV1_EH_SUSPEND_REQUEST:
				printf(" [Suspend Request]");
				break;

			case GTPV1_EH_SUSPEND_RESPONSE:
				printf(" [Suspend Response]");
				break;

			default:
				printf(" [Unknown Header %u]", nexthdr);
			}

			p += hlen - 1;	
			nexthdr = (int)p[0];
			p++;
		}

	}

	hlen = p - cp;

	if (dport == GTPV1_C_PORT || sport == GTPV1_C_PORT)
		gtp_v1_print_ctrl(p, hlen, gh);
	else if (dport == GTPV1_U_PORT || sport == GTPV1_U_PORT)
		gtp_v1_print_user(p, hlen, gh);

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

void
gtp_v1_print_ctrl(const u_char *cp, u_int hlen, struct gtp_v1_hdr *gh)
{
	int len;

	/* Decode GTP control message. */
	printf(" %s", tok2str(gtp_v1_msgtype, "Message Type %u", gh->msgtype));

	len = ntohs(gh->length) - hlen + sizeof(*gh);
	if (vflag)
		gtp_decode_ie(cp, GTP_VERSION_1, len);
}

void
gtp_v1_print_user(const u_char *cp, u_int hlen, struct gtp_v1_hdr *gh)
{
	int len, version;

	/* Decode GTP user message. */
	printf(" %s", tok2str(gtp_v1_msgtype, "Message Type %u", gh->msgtype));

	if (!vflag)
		return;

	len = ntohs(gh->length) - hlen + sizeof(*gh);

	if (gh->msgtype == GTPV1_G_PDU) {

		TCHECK(cp[0]);
		version = cp[0] >> 4;

		printf(" { ");

		if (version == 4)
			ip_print(cp, len);
		else if (version == 6)
			ip6_print(cp, len);
		else
			printf("Unknown IP version %u", version);

		printf(" }");

	} else
		gtp_decode_ie(cp, GTP_VERSION_1, len);

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

void
gtp_v1_print_prime(const u_char *cp, struct gtp_v1_prime_hdr *gph)
{
	int len;
	
	/* Decode GTP prime header. */
	TCHECK(*gph);
	cp += sizeof(struct gtp_v1_prime_hdr);

	len = ntohs(gph->length);
	printf(" (len %u, seq %u) ", len, ntohs(gph->seqno));

	/* Decode GTP message. */
	printf("%s", tok2str(gtp_v1_msgtype, "Message Type %u", gph->msgtype));

	if (vflag)
		gtp_decode_ie(cp, GTP_VERSION_1, len);

	return;

trunc:
	printf(" [|%s]", tok2str(gtp_type, "GTP", gtp_proto));
}

int
gtp_v1_print_tv(const u_char *cp, u_int value)
{
	u_int32_t *dpl;
	u_int16_t *dps;
	u_int8_t data;
	int ielen = -1;

	switch (value) {
	case GTPV1_TV_CAUSE:

		/* 29.060 - 7.7.1 Cause. */
		TCHECK(cp[0]);
		data = (u_int8_t)cp[0];
		ielen = GTPV1_TV_CAUSE_LENGTH;
		printf("Cause: %s", tok2str(gtp_v1_cause, "#%u", data));
		break;

	case GTPV1_TV_IMSI:

		/* 29.060 7.7.2 - International Mobile Subscriber Identity. */
		TCHECK2(cp[0], GTPV1_TV_IMSI_LENGTH - 1);
		printf("IMSI ");
		gtp_print_tbcd(cp, GTPV1_TV_IMSI_LENGTH - 1);
		ielen = GTPV1_TV_IMSI_LENGTH;
		break;

	case GTPV1_TV_RAI:

		/* 29.060 7.7.3 - Routing Area Identity (RAI). */
		TCHECK2(cp[0], GTPV1_TV_RAI_LENGTH - 1);
		printf("RAI: MCC ");
		data = cp[1] | 0xf0;
		gtp_print_tbcd(cp, 1);
		gtp_print_tbcd(&data, 1);
		printf(", MNC ");
		data = (cp[1] >> 4) | 0xf0;
		gtp_print_tbcd(cp + 2, 1);
		gtp_print_tbcd(&data, 1);
		printf(", LAC 0x%x%x", cp[3], cp[4]);
		printf(", RAC 0x%x", cp[5]);
		ielen = GTPV1_TV_RAI_LENGTH;
		break;

	case GTPV1_TV_TLLI:

		/* 29.060 7.7.4 - Temporary Logical Link Identity (TLLI). */
		TCHECK2(cp[0], GTPV1_TV_TLLI_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("TLLI 0x%x", ntohl(*dpl));
		ielen = GTPV1_TV_TLLI_LENGTH;
		break;

	case GTPV1_TV_PTMSI:

		/* 29.060 7.7.5 - Packet TMSI (P-TMSI). */
		TCHECK2(cp[0], GTPV1_TV_PTMSI_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("P-TMSI 0x%x", ntohl(*dpl));
		ielen = GTPV1_TV_PTMSI_LENGTH;
		break;

	case GTPV1_TV_REORDER:

		/* 29.060 7.7.6 - Reordering Required. */
		TCHECK2(cp[0], GTPV1_TV_REORDER_LENGTH - 1);
		printf("Reordering Required: ");
		if (cp[0] & 0x1)
			printf("yes");
		else
			printf("no");
		ielen = GTPV1_TV_REORDER_LENGTH;
		break;

	case GTPV1_TV_AUTH:

		/* 29.060 7.7.7 - Authentication Triplet. */
		TCHECK2(cp[0], GTPV1_TV_AUTH_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("Auth: RAND 0x%x%x%x%x, SRES 0x%x, Kc 0x%x%x",
		    ntohl(dpl[0]), ntohl(dpl[1]), ntohl(dpl[2]), ntohl(dpl[3]),
		    ntohl(dpl[4]), ntohl(dpl[5]), ntohl(dpl[6]));
		ielen = GTPV1_TV_AUTH_LENGTH;
		break;

	case GTPV1_TV_MAP_CAUSE:

		/* 29.060 7.7.8 - MAP Cause. */
		/* Cause defined in 3GPP TS 29.002. */
		TCHECK2(cp[0], GTPV1_TV_MAP_CAUSE_LENGTH - 1);
		printf("Map Cause: %u", cp[0]);
		ielen = GTPV1_TV_MAP_CAUSE_LENGTH;
		break;

	case GTPV1_TV_PTMSI_SIGNATURE:

		/* 29.060 7.7.9 - P-TMSI Signature. */
		/* Signature defined in 3GPP TS 24.008. */
		TCHECK2(cp[0], GTPV1_TV_PTMSI_SIGNATURE_LENGTH - 1);
		printf("PTMSI Signature: 0x%x%x%x", cp[0], cp[1], cp[2]);
		ielen = GTPV1_TV_PTMSI_SIGNATURE_LENGTH;
		break;

	case GTPV1_TV_MS_VALIDATED:

		/* 29.060 7.7.10 - MS Validated. */
		TCHECK2(cp[0], GTPV1_TV_MS_VALIDATED_LENGTH - 1);
		printf("MS Validated: ");
		if (cp[0] & 0x1)
			printf("yes");
		else
			printf("no");
		ielen = GTPV1_TV_MS_VALIDATED_LENGTH;
		break;

	case GTPV1_TV_RECOVERY:

		/* 29.060 7.7.11 - Recovery. */
		TCHECK2(cp[0], GTPV1_TV_RECOVERY_LENGTH - 1);
		printf("Recovery: Restart counter %u", cp[0]);
		ielen = GTPV1_TV_RECOVERY_LENGTH;
		break;

	case GTPV1_TV_SELECTION_MODE:

		/* 29.060 7.7.12 - Selection Mode. */
		TCHECK2(cp[0], GTPV1_TV_SELECTION_MODE_LENGTH - 1);
		data = (u_int8_t)cp[0];
		printf("Selection Mode: %u", data & 0x2);
		ielen = GTPV1_TV_SELECTION_MODE_LENGTH;
		break;

	case GTPV1_TV_TEID_DATA_I:

		/* 29.060 7.7.13 - Tunnel Endpoint Identifier Data I. */
		TCHECK2(cp[0], GTPV1_TV_TEID_DATA_I_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("TEI Data I: %u", ntohl(*dpl));
		ielen = GTPV1_TV_TEID_DATA_I_LENGTH;
		break;

	case GTPV1_TV_TEID_CTRL:

		/* 29.060 7.7.14 - Tunnel Endpoint Identifier Control Plane. */
		TCHECK2(cp[0], GTPV1_TV_TEID_CTRL_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("TEI Control Plane: %u", ntohl(*dpl));
		ielen = GTPV1_TV_TEID_CTRL_LENGTH;
		break;

	case GTPV1_TV_TEID_DATA_II:

		/* 29.060 7.7.15 - Tunnel Endpoint Identifier Data II. */
		TCHECK2(cp[0], GTPV1_TV_TEID_DATA_II_LENGTH - 1);
		data = cp[0] & 0xf;
		dpl = (u_int32_t *)(cp + 1);
		printf("TEI Data II: %u, NSAPI %u", ntohl(*dpl), data);
		ielen = GTPV1_TV_TEID_DATA_II_LENGTH;
		break;

	case GTPV1_TV_TEARDOWN:

		/* 29.060 7.7.16 - Teardown Indicator. */
		TCHECK2(cp[0], GTPV1_TV_TEARDOWN_LENGTH - 1);
		printf("Teardown: ");
		if (cp[0] & 0x1)
			printf("yes");
		else
			printf("no");
		ielen = GTPV1_TV_TEARDOWN_LENGTH;
		break;

	case GTPV1_TV_NSAPI:

		/* 29.060 7.7.17 - NSAPI. */
		TCHECK2(cp[0], GTPV1_TV_NSAPI_LENGTH - 1);
		data = (u_int8_t)cp[0];
		printf("NSAPI %u", data & 0xf);
		ielen = GTPV1_TV_NSAPI_LENGTH;
		break;

	case GTPV1_TV_RANAP:

		/* 29.060 7.7.18 - RANAP Cause. */
		TCHECK2(cp[0], GTPV1_TV_RANAP_LENGTH - 1);
		printf("RANAP Cause: %u", cp[0]);
		ielen = GTPV1_TV_RANAP_LENGTH;
		break;

	case GTPV1_TV_RAB_CONTEXT:

		/* 29.060 7.7.19 - RAB Context. */
		TCHECK2(cp[0], GTPV1_TV_RAB_CONTEXT_LENGTH - 1);
		data = cp[0] & 0xf;
		dps = (u_int16_t *)(cp + 1);
		printf("RAB Context: NSAPI %u, DL GTP-U Seq No %u,"
		    "UL GTP-U Seq No %u, DL PDCP Seq No %u, UL PDCP Seq No %u",
		    data, ntohs(dps[0]), ntohs(dps[1]), ntohs(dps[2]),
		    ntohs(dps[3]));
		ielen = GTPV1_TV_RAB_CONTEXT_LENGTH;
		break;

	case GTPV1_TV_RADIO_PRIORITY_SMS:

		/* 29.060 7.7.20 - Radio Priority SMS. */
		TCHECK2(cp[0], GTPV1_TV_RADIO_PRI_SMS_LENGTH - 1);
		printf("Radio Priority SMS: %u", cp[0] & 0x7);
		ielen = GTPV1_TV_RADIO_PRI_SMS_LENGTH;
		break;

	case GTPV1_TV_RADIO_PRIORITY:

		/* 29.060 7.7.21 - Radio Priority. */
		TCHECK2(cp[0], GTPV1_TV_RADIO_PRI_LENGTH - 1);
		data = cp[0] >> 4;
		printf("Radio Priority: %u, NSAPI %u", cp[0] & 0x7, data);
		ielen = GTPV1_TV_RADIO_PRI_LENGTH;
		break;

	case GTPV1_TV_PACKET_FLOW_ID:

		/* 29.060 7.7.22 - Packet Flow ID. */
		TCHECK2(cp[0], GTPV1_TV_PACKET_FLOW_ID_LENGTH - 1);
		printf("Packet Flow ID: %u, NSAPI %u", cp[1], cp[0] & 0xf);
		ielen = GTPV1_TV_PACKET_FLOW_ID_LENGTH;
		break;

	case GTPV1_TV_CHARGING:

		/* 29.060 7.7.23 - Charging Characteristics. */
		/* Charging defined in 3GPP TS 32.298. */
		TCHECK2(cp[0], GTPV1_TV_CHARGING_LENGTH - 1);
		printf("Charging Characteristics");		/* XXX */
		ielen = GTPV1_TV_CHARGING_LENGTH;
		break;

	case GTPV1_TV_TRACE_REFERENCE:

		/* 29.060 7.7.24 - Trace Reference. */
		TCHECK2(cp[0], GTPV1_TV_TRACE_REFERENCE_LENGTH - 1);
		dps = (u_int16_t *)cp;
		printf("Trace Reference: %u", ntohs(*dps));
		ielen = GTPV1_TV_TRACE_REFERENCE_LENGTH;
		break;

	case GTPV1_TV_TRACE_TYPE:

		/* 29.060 7.7.25 - Trace Type. */
		/* Trace type defined in GSM 12.08. */
		TCHECK2(cp[0], GTPV1_TV_TRACE_TYPE_LENGTH - 1);
		dps = (u_int16_t *)cp;
		printf("Trace Type: %u", ntohs(*dps));
		ielen = GTPV1_TV_TRACE_TYPE_LENGTH;
		break;

	case GTPV1_TV_MSNRR:

		/* 29.060 7.7.26 - MS Not Reachable Reason. */
		/* Reason defined in 3GPP TS 23.040. */
		TCHECK2(cp[0], GTPV1_TV_MSNRR_LENGTH - 1);
		printf("MS NRR: %u", cp[0]);
		ielen = GTPV1_TV_MSNRR_LENGTH;
		break;

	case GTPV1_TV_PACKET_XFER_CMD:

		/* 32.295 6.2.4.5.2 - Packet Transfer Command. */
		TCHECK2(cp[0], GTPV1_TV_PACKET_XFER_CMD_LENGTH - 1);
		printf("Packet Transfer Command");
		gtp_print_str(gtp_packet_xfer_cmd, nitems(gtp_packet_xfer_cmd),
		    cp[0]);
		ielen = GTPV1_TV_PACKET_XFER_CMD_LENGTH;
		break;

	case GTPV1_TV_CHARGING_ID:

		/* 29.060 7.7.26 - Charging ID. */
		TCHECK2(cp[0], GTPV1_TV_CHARGING_ID_LENGTH - 1);
		dpl = (u_int32_t *)cp;
		printf("Charging ID: %u", ntohl(*dpl));
		ielen = GTPV1_TV_CHARGING_ID_LENGTH;
		break;

	default:
		printf("TV %u", value);
	}

trunc:
	return ielen;
}

int
gtp_v1_print_tlv(const u_char *cp, u_int value)
{
	u_int8_t data;
	u_int16_t *lenp, *seqno, len;
	int ielen = -1;

	/* Get length of IE. */
	TCHECK2(cp[0], 2);
	lenp = (u_int16_t *)cp;
	cp += 2;
	len = ntohs(*lenp);
	TCHECK2(cp[0], len);
	ielen = sizeof(data) + sizeof(len) + len;

	switch (value) {
	case GTPV1_TLV_END_USER_ADDRESS:

		/* 3GPP 29.060 - 7.7.27 End User Address. */
		printf("End User Address");
		gtp_print_user_address(cp, len);
		break;

	case GTPV1_TLV_MM_CONTEXT:

		/* 29.060 7.7.28 - MM Context. */
		printf("MM Context");				/* XXX */
		break;

	case GTPV1_TLV_PDP_CONTEXT:

		/* 29.260 7.7.29 - PDP Context. */
		printf("PDP Context");				/* XXX */
		break;

	case GTPV1_TLV_ACCESS_POINT_NAME:

		/* 29.060 7.7.30 - Access Point Name. */
		printf("AP Name: ");
		gtp_print_apn(cp, len);
		break;

	case GTPV1_TLV_PROTOCOL_CONFIG_OPTIONS:

		/* 29.060 7.7.31 - Protocol Configuration Options. */
		/* Defined in 3GPP TS 24.008. */
		printf("Config Options");			/* XXX */
		break;

	case GTPV1_TLV_GSN_ADDRESS:

		/* 29.060 7.7.32 - GSN Address. */
		/* Defined in 3GPP TS 23.003. */
		printf("GSN Address");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		else if (len == 16)
			printf(": %s", ip6addr_string(cp));
		break;

	case GTPV1_TLV_MSISDN:

		/* 29.060 7.7.33 - MS International PSTN/ISDN Number. */
		printf("MSISDN ");
		data = (u_int8_t)cp[0];		/* XXX - Number type. */
		gtp_print_tbcd(cp + 1, len - 1);
		break;

	case GTPV1_TLV_QOS_PROFILE:

		/* 29.060 7.7.34 - QoS Profile. */
		/* QoS profile defined in 3GPP TS 24.008 10.5.6.5. */
		printf("QoS Profile: ");
		data = (u_int8_t)cp[0];
		printf("Delay Class %u, ", (data >> 3) & 0x7);
		printf("Reliability Class %u", data & 0x7);
		if (vflag > 1) {
			printf(", ");
			data = (u_int8_t)cp[1];
			printf("Precedence Class %u", data & 0x7);
			/* XXX - Decode more QoS fields. */
		}
		break;

	case GTPV1_TLV_AUTHENTICATION:

		/* 29.060 7.7.35 - Authentication. */
		printf("Authentication");			/* XXX */
		break;

	case GTPV1_TLV_TRAFFIC_FLOW:

		/* 29.060 7.7.36 - Traffic Flow Template. */
		printf("Traffic Flow Template");		/* XXX */
		break;

	case GTPV1_TLV_TARGET_IDENTIFICATION:

		/* 29.060 7.7.37 - Target Identification. */
		printf("Target ID");				/* XXX */
		break;

	case GTPV1_TLV_UTRAN_CONTAINER:

		/* 29.060 7.7.38 - UTRAN Transparent Container. */
		printf("UTRAN Container");			/* XXX */
		break;

	case GTPV1_TLV_RAB_SETUP_INFORMATION:

		/* 29.060 7.7.39 - RAB Setup Information. */
		printf("RAB Setup");				/* XXX */
		break;

	case GTPV1_TLV_EXT_HEADER_TYPE_LIST:

		/* 29.060 7.7.40 - Extension Header Type List. */
		printf("Extension Header List");		/* XXX */
		break;

	case GTPV1_TLV_TRIGGER_ID:

		/* 29.060 7.7.41 - Trigger ID. */
		printf("Trigger ID");				/* XXX */
		break;

	case GTPV1_TLV_OMC_IDENTITY:

		/* 29.060 7.7.42 - OMC Identity. */
		printf("OMC Identity");				/* XXX */
		break;

	case GTPV1_TLV_RAN_CONTAINER:

		/* 29.060 7.7.43 - RAN Transparent Container. */
		printf("RAN Container");			/* XXX */
		break;

	case GTPV1_TLV_PDP_CONTEXT_PRIORITIZATION:

		/* 29.060 7.7.45 - PDP Context Prioritization. */
		printf("PDP Context Prioritization");		/* XXX */
		break;

	case GTPV1_TLV_ADDITIONAL_RAB_SETUP_INFO:

		/* 29.060 7.7.45A - Additional RAB Setup Information. */
		printf("Additional RAB Setup");			/* XXX */
		break;

	case GTPV1_TLV_SGSN_NUMBER:

		/* 29.060 7.7.47 - SGSN Number. */
		printf("SGSN Number");				/* XXX */
		break;

	case GTPV1_TLV_COMMON_FLAGS:

		/* 29.060 7.7.48 - Common Flags. */
		printf("Common Flags");				/* XXX */
		break;

	case GTPV1_TLV_APN_RESTRICTION:

		/* 29.060 7.7.49 - APN Restriction. */
		data = (u_int8_t)cp[0];
		printf("APN Restriction: %u", data);
		break;

	case GTPV1_TLV_RADIO_PRIORITY_LCS:

		/* 29.060 7.7.25B - Radio Priority LCS. */
		printf("Radio Priority LCS: %u", cp[0] & 0x7);
		break;

	case GTPV1_TLV_RAT_TYPE:

		/* 29.060 7.7.50 - RAT Type. */
		printf("RAT");
		gtp_print_str(gtp_rat_type, nitems(gtp_rat_type), cp[0]);
		break;

	case GTPV1_TLV_USER_LOCATION_INFO:

		/* 29.060 7.7.51 - User Location Information. */
		printf("ULI");					/* XXX */
		break;

	case GTPV1_TLV_MS_TIME_ZONE:

		/* 29.060 7.7.52 - MS Time Zone. */
		printf("MSTZ");					/* XXX */
		break;

	case GTPV1_TLV_IMEI_SV:

		/* 29.060 7.7.53 - IMEI(SV). */
		printf("IMEI(SV) ");
		gtp_print_tbcd(cp, len);
		break;

	case GTPV1_TLV_CAMEL_CHARGING_CONTAINER:

		/* 29.060 7.7.54 - CAMEL Charging Information Container. */
		printf("CAMEL Charging");			/* XXX */
		break;

	case GTPV1_TLV_MBMS_UE_CONTEXT:

		/* 29.060 7.7.55 - MBMS UE Context. */
		printf("MBMS UE Context");			/* XXX */
		break;

	case GTPV1_TLV_TMGI:

		/* 29.060 7.7.56 - Temporary Mobile Group Identity. */
		printf("TMGI");					/* XXX */
		break;

	case GTPV1_TLV_RIM_ROUTING_ADDRESS:

		/* 29.060 7.7.57 - RIM Routing Address. */
		printf("RIM Routing Address");			/* XXX */
		break;

	case GTPV1_TLV_MBMS_PROTOCOL_CONFIG_OPTIONS:

		/* 29.060 7.7.58 - MBMS Protocol Configuration Options. */
		printf("MBMS Protocol Config Options");		/* XXX */
		break;

	case GTPV1_TLV_MBMS_SERVICE_AREA:

		/* 29.060 7.7.60 - MBMS Service Area. */
		printf("MBMS Service Area");			/* XXX */
		break;

	case GTPV1_TLV_SOURCE_RNC_PDCP_CONTEXT_INFO:

		/* 29.060 7.7.61 - Source RNC PDCP Context Information. */
		printf("Source RNC PDCP Context");		/* XXX */
		break;

	case GTPV1_TLV_ADDITIONAL_TRACE_INFO:

		/* 29.060 7.7.62 - Additional Trace Information. */
		printf("Additional Trace Info");		/* XXX */
		break;

	case GTPV1_TLV_HOP_COUNTER:

		/* 29.060 7.7.63 - Hop Counter. */
		printf("Hop Counter: %u", cp[0]);
		break;

	case GTPV1_TLV_SELECTED_PLMN_ID:

		/* 29.060 7.7.64 - Selected PLMN ID. */
		printf("Selected PLMN ID");			/* XXX */
		break;

	case GTPV1_TLV_MBMS_SESSION_IDENTIFIER:

		/* 29.060 7.7.65 - MBMS Session Identifier. */
		printf("MBMS Session ID: %u", cp[0]);
		break;

	case GTPV1_TLV_MBMS_2G_3G_INDICATOR:

		/* 29.060 7.7.66 - MBMS 2G/3G Indicator. */
		printf("MBMS 2G/3G Indicator");
		gtp_print_str(mbms_2g3g_indicator, nitems(mbms_2g3g_indicator),
		    cp[0]);
		break;

	case GTPV1_TLV_ENHANCED_NSAPI:

		/* 29.060 7.7.67 - Enhanced NSAPI. */
		printf("Enhanced NSAPI");			/* XXX */
		break;

	case GTPV1_TLV_MBMS_SESSION_DURATION:

		/* 29.060 7.7.59 - MBMS Session Duration. */
		printf("MBMS Session Duration");		/* XXX */
		break;

	case GTPV1_TLV_ADDITIONAL_MBMS_TRACE_INFO:

		/* 29.060 7.7.68 - Additional MBMS Trace Info. */
		printf("Additional MBMS Trace Info");		/* XXX */
		break;

	case GTPV1_TLV_MBMS_SESSION_REPITITION_NO:

		/* 29.060 7.7.69 - MBMS Session Repetition Number. */
		printf("MBMS Session Repetition No: %u", cp[0]);
		break;

	case GTPV1_TLV_MBMS_TIME_TO_DATA_TRANSFER:

		/* 29.060 7.7.70 - MBMS Time to Data Transfer. */
		printf("MBMS Time to Data Transfer: %u", cp[0]);
		break;

	case GTPV1_TLV_PS_HANDOVER_REQUEST_CONTEXT:

		/* 29.060 7.7.71 - PS Handover Request Context (Void). */
		break;

	case GTPV1_TLV_BSS_CONTAINER:

		/* 29.060 7.7.72 - BSS Container. */
		printf("BSS Container");			/* XXX */
		break;

	case GTPV1_TLV_CELL_IDENTIFICATION:

		/* 29.060 7.7.73 - Cell Identification. */
		printf("Cell Identification");			/* XXX */
		break;

	case GTPV1_TLV_PDU_NUMBERS:

		/* 29.060 7.7.74 - PDU Numbers. */
		printf("PDU Numbers");				/* XXX */
		break;

	case GTPV1_TLV_BSSGP_CAUSE:

		/* 29.060 7.7.75 - BSSGP Cause. */
		printf("BSSGP Cause: %u", cp[0]);
		break;

	case GTPV1_TLV_REQUIRED_MBMS_BEARER_CAP:

		/* 29.060 7.7.76 - Required MBMS Bearer Cap. */
		printf("Required MBMS Bearer Cap");		/* XXX */
		break;

	case GTPV1_TLV_RIM_ROUTING_ADDRESS_DISC:

		/* 29.060 7.7.77 - RIM Routing Address Discriminator. */
		printf("RIM Routing Address Discriminator: %u", cp[0] & 0xf);
		break;

	case GTPV1_TLV_LIST_OF_SETUP_PFCS:

		/* 29.060 7.7.78 - List of Setup PFCs. */
		printf("List of Setup PFCs");			/* XXX */
		break;

	case GTPV1_TLV_PS_HANDOVER_XID_PARAMETERS:

		/* 29.060 7.7.79 - PS Handover XID Parameters. */
		printf("PS Handover XID Parameters");		/* XXX */
		break;

	case GTPV1_TLV_MS_INFO_CHANGE_REPORTING:

		/* 29.060 7.7.80 - MS Info Change Reporting. */
		printf("MS Info Change Reporting");
		gtp_print_str(ms_info_change_rpt, nitems(ms_info_change_rpt),
		    cp[0]);
		break;

	case GTPV1_TLV_DIRECT_TUNNEL_FLAGS:

		/* 29.060 7.7.81 - Direct Tunnel Flags. */
		printf("Direct Tunnel Flags");			/* XXX */
		break;

	case GTPV1_TLV_CORRELATION_ID:

		/* 29.060 7.7.82 - Correlation ID. */
		printf("Correlation ID");			/* XXX */
		break;

	case GTPV1_TLV_BEARER_CONTROL_MODE:

		/* 29.060 7.7.83 - Bearer Control Mode. */
		printf("Bearer Control Mode");			/* XXX */
		break;

	case GTPV1_TLV_MBMS_FLOW_IDENTIFIER:

		/* 29.060 7.7.84 - MBMS Flow Identifier. */
		printf("MBMS Flow Identifier");			/* XXX */
		break;

	case GTPV1_TLV_RELEASED_PACKETS:

		/* 32.295 6.2.4.5.4 - Sequence Numbers of Released Packets. */
		printf("Released Packets:");
		seqno = (u_int16_t *)cp;
		while (len > 0) {
			printf(" %u", ntohs(*seqno));
			seqno++;
			len -= sizeof(*seqno);
		}
		break;

	case GTPV1_TLV_CANCELLED_PACKETS:

		/* 32.295 6.2.4.5.5 - Sequence Numbers of Cancelled Packets. */
		printf("Cancelled Packets:");
		seqno = (u_int16_t *)cp;
		while (len > 0) {
			printf(" %u", ntohs(*seqno));
			seqno++;
			len -= sizeof(*seqno);
		}
		break;

	case GTPV1_TLV_CHARGING_GATEWAY_ADDRESS:

		/* 29.060 7.7.44 - Charging Gateway Address. */
		printf("Charging Gateway");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		else if (len == 16)
			printf(": %s", ip6addr_string(cp));
		break;

	case GTPV1_TLV_DATA_RECORD_PACKET:

		/* 32.295 6.2.4.5.3 - Data Record Packet. */
		printf("Data Record: Records %u, Format %u, Format Version %u",
		    cp[0], cp[1], ntohs(*(u_int16_t *)(cp + 2)));
		break;

	case GTPV1_TLV_REQUESTS_RESPONDED:

		/* 32.295 6.2.4.6 - Requests Responded. */
		printf("Requests Responded:");
		seqno = (u_int16_t *)cp;
		while (len > 0) {
			printf(" %u", ntohs(*seqno));
			seqno++;
			len -= sizeof(*seqno);
		}
		break;

	case GTPV1_TLV_ADDRESS_OF_RECOMMENDED_NODE:

		/* 32.295 6.2.4.3 - Address of Recommended Node. */
		printf("Address of Recommended Node");
		if (len == 4)
			printf(": %s", ipaddr_string(cp));
		else if (len == 16)
			printf(": %s", ip6addr_string(cp));
		break;

	case GTPV1_TLV_PRIVATE_EXTENSION:

		/* 29.060 7.7.46 - Private Extension. */
		printf("Private Extension");
		break;

	default:
		printf("TLV %u (len %u)", value, len);
	}

	return ielen;

trunc:
	return -1;
}