#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);
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 }
};
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 }
};
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 }
};
static struct tok gtp_v1_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" },
{ 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" },
{ 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;
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]");
}
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:
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));
}
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++;
}
}
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);
}
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(".");
}
}
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]);
}
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;
TCHECK(gh->flags);
if ((gh->flags & GTPV0_HDR_PROTO_TYPE) == 0) {
gtp_proto = GTP_V0_PRIME_PROTO;
gtp_v0_print_prime(cp);
return;
}
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));
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;
TCHECK(*gph);
cp += sizeof(*gph);
len = ntohs(gph->length);
printf("GTPv0' (len %u, seq %u) ", len, ntohs(gph->seqno));
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:
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:
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:
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:
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:
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:
TCHECK2(cp[0], GTPV0_TV_QOS_LENGTH - 1);
printf("QoS Profile");
ielen = GTPV0_TV_QOS_LENGTH;
break;
case GTPV0_TV_REORDER:
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:
TCHECK2(cp[0], GTPV0_TV_AUTH_TRIPLET_LENGTH - 1);
printf("Authentication");
ielen = GTPV0_TV_AUTH_TRIPLET_LENGTH;
break;
case GTPV0_TV_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:
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:
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:
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:
TCHECK2(cp[0], GTPV0_TV_SELECTION_MODE_LENGTH - 1);
printf("Selection Mode");
ielen = GTPV0_TV_SELECTION_MODE_LENGTH;
break;
case GTPV0_TV_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:
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:
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:
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:
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;
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:
printf("End User Address");
gtp_print_user_address(cp, len);
break;
case GTPV0_TLV_MM_CONTEXT:
printf("MM Context");
break;
case GTPV0_TLV_PDP_CONTEXT:
printf("PDP Context");
break;
case GTPV0_TLV_ACCESS_POINT_NAME:
printf("AP Name: ");
gtp_print_apn(cp, len);
break;
case GTPV0_TLV_PROTOCOL_CONFIG_OPTIONS:
printf("Protocol Configuration Options");
break;
case GTPV0_TLV_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:
printf("MSISDN ");
data = (u_int8_t)cp[0];
gtp_print_tbcd(cp + 1, len - 1);
break;
case GTPV0_TLV_CHARGING_GATEWAY_ADDRESS:
printf("Charging Gateway");
if (len == 4)
printf(": %s", ipaddr_string(cp));
break;
case GTPV0_TLV_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:
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:
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;
}
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'");
}
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) {
nexthdr = ghe->nexthdr;
while (nexthdr != GTPV1_EH_NONE) {
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;
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;
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;
TCHECK(*gph);
cp += sizeof(struct gtp_v1_prime_hdr);
len = ntohs(gph->length);
printf(" (len %u, seq %u) ", len, ntohs(gph->seqno));
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
TCHECK2(cp[0], GTPV1_TV_CHARGING_LENGTH - 1);
printf("Charging Characteristics");
ielen = GTPV1_TV_CHARGING_LENGTH;
break;
case GTPV1_TV_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:
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:
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:
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:
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;
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:
printf("End User Address");
gtp_print_user_address(cp, len);
break;
case GTPV1_TLV_MM_CONTEXT:
printf("MM Context");
break;
case GTPV1_TLV_PDP_CONTEXT:
printf("PDP Context");
break;
case GTPV1_TLV_ACCESS_POINT_NAME:
printf("AP Name: ");
gtp_print_apn(cp, len);
break;
case GTPV1_TLV_PROTOCOL_CONFIG_OPTIONS:
printf("Config Options");
break;
case GTPV1_TLV_GSN_ADDRESS:
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:
printf("MSISDN ");
data = (u_int8_t)cp[0];
gtp_print_tbcd(cp + 1, len - 1);
break;
case GTPV1_TLV_QOS_PROFILE:
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);
}
break;
case GTPV1_TLV_AUTHENTICATION:
printf("Authentication");
break;
case GTPV1_TLV_TRAFFIC_FLOW:
printf("Traffic Flow Template");
break;
case GTPV1_TLV_TARGET_IDENTIFICATION:
printf("Target ID");
break;
case GTPV1_TLV_UTRAN_CONTAINER:
printf("UTRAN Container");
break;
case GTPV1_TLV_RAB_SETUP_INFORMATION:
printf("RAB Setup");
break;
case GTPV1_TLV_EXT_HEADER_TYPE_LIST:
printf("Extension Header List");
break;
case GTPV1_TLV_TRIGGER_ID:
printf("Trigger ID");
break;
case GTPV1_TLV_OMC_IDENTITY:
printf("OMC Identity");
break;
case GTPV1_TLV_RAN_CONTAINER:
printf("RAN Container");
break;
case GTPV1_TLV_PDP_CONTEXT_PRIORITIZATION:
printf("PDP Context Prioritization");
break;
case GTPV1_TLV_ADDITIONAL_RAB_SETUP_INFO:
printf("Additional RAB Setup");
break;
case GTPV1_TLV_SGSN_NUMBER:
printf("SGSN Number");
break;
case GTPV1_TLV_COMMON_FLAGS:
printf("Common Flags");
break;
case GTPV1_TLV_APN_RESTRICTION:
data = (u_int8_t)cp[0];
printf("APN Restriction: %u", data);
break;
case GTPV1_TLV_RADIO_PRIORITY_LCS:
printf("Radio Priority LCS: %u", cp[0] & 0x7);
break;
case GTPV1_TLV_RAT_TYPE:
printf("RAT");
gtp_print_str(gtp_rat_type, nitems(gtp_rat_type), cp[0]);
break;
case GTPV1_TLV_USER_LOCATION_INFO:
printf("ULI");
break;
case GTPV1_TLV_MS_TIME_ZONE:
printf("MSTZ");
break;
case GTPV1_TLV_IMEI_SV:
printf("IMEI(SV) ");
gtp_print_tbcd(cp, len);
break;
case GTPV1_TLV_CAMEL_CHARGING_CONTAINER:
printf("CAMEL Charging");
break;
case GTPV1_TLV_MBMS_UE_CONTEXT:
printf("MBMS UE Context");
break;
case GTPV1_TLV_TMGI:
printf("TMGI");
break;
case GTPV1_TLV_RIM_ROUTING_ADDRESS:
printf("RIM Routing Address");
break;
case GTPV1_TLV_MBMS_PROTOCOL_CONFIG_OPTIONS:
printf("MBMS Protocol Config Options");
break;
case GTPV1_TLV_MBMS_SERVICE_AREA:
printf("MBMS Service Area");
break;
case GTPV1_TLV_SOURCE_RNC_PDCP_CONTEXT_INFO:
printf("Source RNC PDCP Context");
break;
case GTPV1_TLV_ADDITIONAL_TRACE_INFO:
printf("Additional Trace Info");
break;
case GTPV1_TLV_HOP_COUNTER:
printf("Hop Counter: %u", cp[0]);
break;
case GTPV1_TLV_SELECTED_PLMN_ID:
printf("Selected PLMN ID");
break;
case GTPV1_TLV_MBMS_SESSION_IDENTIFIER:
printf("MBMS Session ID: %u", cp[0]);
break;
case GTPV1_TLV_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:
printf("Enhanced NSAPI");
break;
case GTPV1_TLV_MBMS_SESSION_DURATION:
printf("MBMS Session Duration");
break;
case GTPV1_TLV_ADDITIONAL_MBMS_TRACE_INFO:
printf("Additional MBMS Trace Info");
break;
case GTPV1_TLV_MBMS_SESSION_REPITITION_NO:
printf("MBMS Session Repetition No: %u", cp[0]);
break;
case GTPV1_TLV_MBMS_TIME_TO_DATA_TRANSFER:
printf("MBMS Time to Data Transfer: %u", cp[0]);
break;
case GTPV1_TLV_PS_HANDOVER_REQUEST_CONTEXT:
break;
case GTPV1_TLV_BSS_CONTAINER:
printf("BSS Container");
break;
case GTPV1_TLV_CELL_IDENTIFICATION:
printf("Cell Identification");
break;
case GTPV1_TLV_PDU_NUMBERS:
printf("PDU Numbers");
break;
case GTPV1_TLV_BSSGP_CAUSE:
printf("BSSGP Cause: %u", cp[0]);
break;
case GTPV1_TLV_REQUIRED_MBMS_BEARER_CAP:
printf("Required MBMS Bearer Cap");
break;
case GTPV1_TLV_RIM_ROUTING_ADDRESS_DISC:
printf("RIM Routing Address Discriminator: %u", cp[0] & 0xf);
break;
case GTPV1_TLV_LIST_OF_SETUP_PFCS:
printf("List of Setup PFCs");
break;
case GTPV1_TLV_PS_HANDOVER_XID_PARAMETERS:
printf("PS Handover XID Parameters");
break;
case GTPV1_TLV_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:
printf("Direct Tunnel Flags");
break;
case GTPV1_TLV_CORRELATION_ID:
printf("Correlation ID");
break;
case GTPV1_TLV_BEARER_CONTROL_MODE:
printf("Bearer Control Mode");
break;
case GTPV1_TLV_MBMS_FLOW_IDENTIFIER:
printf("MBMS Flow Identifier");
break;
case GTPV1_TLV_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:
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:
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:
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:
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:
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:
printf("Private Extension");
break;
default:
printf("TLV %u (len %u)", value, len);
}
return ielen;
trunc:
return -1;
}