diff --git a/scripts/base/frameworks/tunnels/main.bro b/scripts/base/frameworks/tunnels/main.bro index a3db7061d3..a8fc6c8236 100644 --- a/scripts/base/frameworks/tunnels/main.bro +++ b/scripts/base/frameworks/tunnels/main.bro @@ -88,10 +88,10 @@ redef dpd_config += { [ANALYZER_AYIYA] = [$ports = ayiya_ports] }; const teredo_ports = { 3544/udp }; redef dpd_config += { [ANALYZER_TEREDO] = [$ports = teredo_ports] }; -const gtpv1u_ports = { 2152/udp }; -redef dpd_config += { [ANALYZER_GTPV1] = [$ports = gtpv1u_ports] }; +const gtpv1_ports = { 2152/udp, 2123/udp }; +redef dpd_config += { [ANALYZER_GTPV1] = [$ports = gtpv1_ports] }; -redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1u_ports }; +redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1_ports }; event bro_init() &priority=5 { diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 23321a35dd..b6187df0d9 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -1488,6 +1488,146 @@ type gtpv1_hdr: record { next_type: count &optional; }; +type gtp_cause: count; +type gtp_imsi: count; +type gtp_teardown_ind: bool; +type gtp_nsapi: count; +type gtp_recovery: count; +type gtp_teid1: count; +type gtp_teid_control_plane: count; +type gtp_charging_id: count; +type gtp_charging_gateway_addr: addr; +type gtp_trace_reference: count; +type gtp_trace_type: count; +type gtp_tft: string; +type gtp_trigger_id: string; +type gtp_omc_id: string; +type gtp_reordering_required: bool; +type gtp_proto_config_options: string; +type gtp_charging_characteristics: count; +type gtp_selection_mode: count; +type gtp_access_point_name: string; +type gtp_msisdn: string; + +type gtp_gsn_addr: record { + ## If the GSN Address information element has length 4 or 16, then this + ## field is set to be the informational element's value interpreted as + ## an IPv4 or IPv6 address, respectively. + ip: addr &optional; + ## This field is set if it's not an IPv4 or IPv6 address. + other: string &optional; +}; + +type gtp_end_user_addr: record { + pdp_type_org: count; + pdp_type_num: count; + ## Set if the End User Address information element is IPv4/IPv6. + pdp_ip: addr &optional; + ## Set if the End User Address information element isn't IPv4/IPv6. + pdp_other_addr: string &optional; +}; + +type gtp_rai: record { + mcc: count; + mnc: count; + lac: count; + rac: count; +}; + +type gtp_qos_profile: record { + priority: count; + data: string; +}; + +type gtp_private_extension: record { + id: count; + value: string; +}; + +type gtp_create_pdp_ctx_request_elements: record { + imsi: gtp_imsi &optional; + rai: gtp_rai &optional; + recovery: gtp_recovery &optional; + select_mode: gtp_selection_mode &optional; + data1: gtp_teid1; + cp: gtp_teid_control_plane &optional; + nsapi: gtp_nsapi; + linked_nsapi: gtp_nsapi &optional; + charge_character: gtp_charging_characteristics &optional; + trace_ref: gtp_trace_reference &optional; + trace_type: gtp_trace_type &optional; + end_user_addr: gtp_end_user_addr &optional; + ap_name: gtp_access_point_name &optional; + opts: gtp_proto_config_options &optional; + signal_addr: gtp_gsn_addr; + user_addr: gtp_gsn_addr; + msisdn: gtp_msisdn &optional; + qos_prof: gtp_qos_profile; + tft: gtp_tft &optional; + trigger_id: gtp_trigger_id &optional; + omc_id: gtp_omc_id &optional; + ext: gtp_private_extension &optional; +}; + +type gtp_create_pdp_ctx_response_elements: record { + cause: gtp_cause; + reorder_req: gtp_reordering_required &optional; + recovery: gtp_recovery &optional; + data1: gtp_teid1 &optional; + cp: gtp_teid_control_plane &optional; + charging_id: gtp_charging_id &optional; + end_user_addr: gtp_end_user_addr &optional; + opts: gtp_proto_config_options &optional; + cp_addr: gtp_gsn_addr &optional; + user_addr: gtp_gsn_addr &optional; + qos_prof: gtp_qos_profile &optional; + charge_gateway: gtp_charging_gateway_addr &optional; + ext: gtp_private_extension &optional; +}; + +type gtp_update_pdp_ctx_request_elements: record { + imsi: gtp_imsi &optional; + rai: gtp_rai &optional; + recovery: gtp_recovery &optional; + data1: gtp_teid1; + cp: gtp_teid_control_plane &optional; + nsapi: gtp_nsapi; + trace_ref: gtp_trace_reference &optional; + trace_type: gtp_trace_type &optional; + cp_addr: gtp_gsn_addr; + user_addr: gtp_gsn_addr; + qos_prof: gtp_qos_profile; + tft: gtp_tft &optional; + trigger_id: gtp_trigger_id &optional; + omc_id: gtp_omc_id &optional; + ext: gtp_private_extension &optional; + end_user_addr: gtp_end_user_addr &optional; +}; + +type gtp_update_pdp_ctx_response_elements: record { + cause: gtp_cause; + recovery: gtp_recovery &optional; + data1: gtp_teid1 &optional; + cp: gtp_teid_control_plane &optional; + charging_id: gtp_charging_id &optional; + cp_addr: gtp_gsn_addr &optional; + user_addr: gtp_gsn_addr &optional; + qos_prof: gtp_qos_profile &optional; + charge_gateway: gtp_charging_gateway_addr &optional; + ext: gtp_private_extension &optional; +}; + +type gtp_delete_pdp_ctx_request_elements: record { + teardown_ind: gtp_teardown_ind &optional; + nsapi: gtp_nsapi; + ext: gtp_private_extension &optional; +}; + +type gtp_delete_pdp_ctx_response_elements: record { + cause: gtp_cause; + ext: gtp_private_extension &optional; +}; + ## Definition of "secondary filters". A secondary filter is a BPF filter given as ## index in this table. For each such filter, the corresponding event is raised for ## all matching packets. diff --git a/src/NetVar.cc b/src/NetVar.cc index 1783130f34..248ae15e1a 100644 --- a/src/NetVar.cc +++ b/src/NetVar.cc @@ -5,7 +5,6 @@ #include "Var.h" #include "NetVar.h" -RecordType* gtpv1_hdr_type; RecordType* conn_id; RecordType* endpoint; RecordType* endpoint_stats; @@ -309,7 +308,6 @@ void init_net_var() #include "input.bif.netvar_init" #include "reporter.bif.netvar_init" - gtpv1_hdr_type = internal_type("gtpv1_hdr")->AsRecordType(); conn_id = internal_type("conn_id")->AsRecordType(); endpoint = internal_type("endpoint")->AsRecordType(); endpoint_stats = internal_type("endpoint_stats")->AsRecordType(); diff --git a/src/NetVar.h b/src/NetVar.h index 4bb2d2a7f9..2561fa0ad9 100644 --- a/src/NetVar.h +++ b/src/NetVar.h @@ -8,7 +8,6 @@ #include "EventRegistry.h" #include "Stats.h" -extern RecordType* gtpv1_hdr_type; extern RecordType* conn_id; extern RecordType* endpoint; extern RecordType* endpoint_stats; diff --git a/src/event.bif b/src/event.bif index 8dd940f38b..393021024a 100644 --- a/src/event.bif +++ b/src/event.bif @@ -577,6 +577,13 @@ event teredo_origin_indication%(outer: connection, inner: teredo_hdr%); ## it may become particularly expensive for real-time analysis. event teredo_bubble%(outer: connection, inner: teredo_hdr%); +## Generated for any GTP message with a GTPv1 header. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +event gtpv1_message%(c: connection, hdr: gtpv1_hdr%); + ## Generated for GTPv1 G-PDU packets. That is, packets with a UDP payload ## that includes a GTP header followed by an IPv4 or IPv6 packet. ## @@ -590,6 +597,60 @@ event teredo_bubble%(outer: connection, inner: teredo_hdr%); ## it may become particularly expensive for real-time analysis. event gtpv1_g_pdu_packet%(outer: connection, inner_gtp: gtpv1_hdr, inner_ip: pkt_hdr%); +## Generated for GTPv1-C Create PDP Context Request messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_create_pdp_ctx_request%(c: connection, hdr: gtpv1_hdr, elements: gtp_create_pdp_ctx_request_elements%); + +## Generated for GTPv1-C Create PDP Context Response messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_create_pdp_ctx_response%(c: connection, hdr: gtpv1_hdr, elements: gtp_create_pdp_ctx_response_elements%); + +## Generated for GTPv1-C Update PDP Context Request messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_update_pdp_ctx_request%(c: connection, hdr: gtpv1_hdr, elements: gtp_update_pdp_ctx_request_elements%); + +## Generated for GTPv1-C Update PDP Context Response messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_update_pdp_ctx_response%(c: connection, hdr: gtpv1_hdr, elements: gtp_update_pdp_ctx_response_elements%); + +## Generated for GTPv1-C Delete PDP Context Request messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_delete_pdp_ctx_request%(c: connection, hdr: gtpv1_hdr, elements: gtp_delete_pdp_ctx_request_elements%); + +## Generated for GTPv1-C Delete PDP Context Response messages. +## +## c: The connection over which the message is sent. +## +## hdr: The GTPv1 header. +## +## elements: The set of Information Elements comprising the message. +event gtpv1_delete_pdp_ctx_response%(c: connection, hdr: gtpv1_hdr, elements: gtp_delete_pdp_ctx_response_elements%); + ## Generated for every packet that has a non-empty transport-layer payload. ## This is a very low-level and expensive event that should be avoided when ## at all possible. It's usually infeasible to handle when processing even diff --git a/src/gtpv1-analyzer.pac b/src/gtpv1-analyzer.pac index 08fbdef74a..51d28123cb 100644 --- a/src/gtpv1-analyzer.pac +++ b/src/gtpv1-analyzer.pac @@ -1,4 +1,602 @@ +%code{ +RecordVal* BuildGTPv1Hdr(const GTPv1_Header* pdu) + { + RecordVal* rv = new RecordVal(BifType::Record::gtpv1_hdr); + + rv->Assign(0, new Val(pdu->version(), TYPE_COUNT)); + rv->Assign(1, new Val(pdu->pt_flag(), TYPE_BOOL)); + rv->Assign(2, new Val(pdu->rsv(), TYPE_BOOL)); + rv->Assign(3, new Val(pdu->e_flag(), TYPE_BOOL)); + rv->Assign(4, new Val(pdu->s_flag(), TYPE_BOOL)); + rv->Assign(5, new Val(pdu->pn_flag(), TYPE_BOOL)); + rv->Assign(6, new Val(pdu->msg_type(), TYPE_COUNT)); + rv->Assign(7, new Val(pdu->length(), TYPE_COUNT)); + rv->Assign(8, new Val(pdu->teid(), TYPE_COUNT)); + + if ( pdu->has_opt() ) + { + rv->Assign(9, new Val(pdu->opt_hdr()->seq(), TYPE_COUNT)); + rv->Assign(10, new Val(pdu->opt_hdr()->n_pdu(), TYPE_COUNT)); + rv->Assign(11, new Val(pdu->opt_hdr()->next_type(), TYPE_COUNT)); + } + + return rv; + } + +Val* BuildIMSI(const InformationElement* ie) + { + return new Val(ie->imsi()->value(), TYPE_COUNT); + } + +Val* BuildRAI(const InformationElement* ie) + { + RecordVal* ev = new RecordVal(BifType::Record::gtp_rai); + ev->Assign(0, new Val(ie->rai()->mcc(), TYPE_COUNT)); + ev->Assign(1, new Val(ie->rai()->mnc(), TYPE_COUNT)); + ev->Assign(2, new Val(ie->rai()->lac(), TYPE_COUNT)); + ev->Assign(3, new Val(ie->rai()->rac(), TYPE_COUNT)); + return ev; + } + +Val* BuildRecovery(const InformationElement* ie) + { + return new Val(ie->recovery()->restart_counter(), TYPE_COUNT); + } + +Val* BuildSelectionMode(const InformationElement* ie) + { + return new Val(ie->selection_mode()->mode(), TYPE_COUNT); + } + +Val* BuildTEID1(const InformationElement* ie) + { + return new Val(ie->teid1()->value(), TYPE_COUNT); + } + +Val* BuildTEID_ControlPlane(const InformationElement* ie) + { + return new Val(ie->teidcp()->value(), TYPE_COUNT); + } + +Val* BuildNSAPI(const InformationElement* ie) + { + return new Val(ie->nsapi()->nsapi(), TYPE_COUNT); + } + +Val* BuildChargingCharacteristics(const InformationElement* ie) + { + return new Val(ie->charging_characteristics()->value(), TYPE_COUNT); + } + +Val* BuildTraceReference(const InformationElement* ie) + { + return new Val(ie->trace_reference()->value(), TYPE_COUNT); + } + +Val* BuildTraceType(const InformationElement* ie) + { + return new Val(ie->trace_type()->value(), TYPE_COUNT); + } + +Val* BuildEndUserAddr(const InformationElement* ie) + { + RecordVal* ev = new RecordVal(BifType::Record::gtp_end_user_addr); + ev->Assign(0, new Val(ie->end_user_addr()->pdp_type_org(), TYPE_COUNT)); + ev->Assign(1, new Val(ie->end_user_addr()->pdp_type_num(), TYPE_COUNT)); + + int len = ie->end_user_addr()->pdp_addr().length(); + + if ( len > 0 ) + { + const uint8* d = ie->end_user_addr()->pdp_addr().data(); + + switch ( ie->end_user_addr()->pdp_type_num() ) { + case 0x21: + ev->Assign(2, new AddrVal( + IPAddr(IPv4, (const uint32*) d, IPAddr::Network))); + break; + case 0x57: + ev->Assign(2, new AddrVal( + IPAddr(IPv6, (const uint32*) d, IPAddr::Network))); + break; + default: + ev->Assign(3, new StringVal( + new BroString((const u_char*) d, len, 0))); + break; + } + } + + return ev; + } + +Val* BuildAccessPointName(const InformationElement* ie) + { + BroString* bs = new BroString((const u_char*) ie->ap_name()->value().data(), + ie->ap_name()->value().length(), 0); + return new StringVal(bs); + } + +Val* BuildProtoConfigOptions(const InformationElement* ie) + { + const u_char* d = (const u_char*) ie->proto_config_opts()->value().data(); + int len = ie->proto_config_opts()->value().length(); + return new StringVal(new BroString(d, len, 0)); + } + +Val* BuildGSN_Addr(const InformationElement* ie) + { + RecordVal* ev = new RecordVal(BifType::Record::gtp_gsn_addr); + + int len = ie->gsn_addr()->value().length(); + const uint8* d = ie->gsn_addr()->value().data(); + + if ( len == 4 ) + ev->Assign(0, new AddrVal( + IPAddr(IPv4, (const uint32*) d, IPAddr::Network))); + else if ( len == 16 ) + ev->Assign(0, new AddrVal( + IPAddr(IPv6, (const uint32*) d, IPAddr::Network))); + else + ev->Assign(1, new StringVal(new BroString((const u_char*) d, len, 0))); + + return ev; + } + +Val* BuildMSISDN(const InformationElement* ie) + { + const u_char* d = (const u_char*) ie->msisdn()->value().data(); + int len = ie->msisdn()->value().length(); + return new StringVal(new BroString(d, len, 0)); + } + +Val* BuildQoS_Profile(const InformationElement* ie) + { + RecordVal* ev = new RecordVal(BifType::Record::gtp_qos_profile); + + const u_char* d = (const u_char*) ie->qos_profile()->data().data(); + int len = ie->qos_profile()->data().length(); + + ev->Assign(0, new Val(ie->qos_profile()->alloc_retention_priority(), + TYPE_COUNT)); + ev->Assign(1, new StringVal(new BroString(d, len, 0))); + + return ev; + } + +Val* BuildTrafficFlowTemplate(const InformationElement* ie) + { + const uint8* d = ie->traffic_flow_template()->value().data(); + int len = ie->traffic_flow_template()->value().length(); + return new StringVal(new BroString((const u_char*) d, len, 0)); + } + +Val* BuildTriggerID(const InformationElement* ie) + { + const uint8* d = ie->trigger_id()->value().data(); + int len = ie->trigger_id()->value().length(); + return new StringVal(new BroString((const u_char*) d, len, 0)); + } + +Val* BuildOMC_ID(const InformationElement* ie) + { + const uint8* d = ie->omc_id()->value().data(); + int len = ie->omc_id()->value().length(); + return new StringVal(new BroString((const u_char*) d, len, 0)); + } + +Val* BuildPrivateExt(const InformationElement* ie) + { + RecordVal* ev = new RecordVal(BifType::Record::gtp_private_extension); + + const uint8* d = ie->private_ext()->value().data(); + int len = ie->private_ext()->value().length(); + + ev->Assign(0, new Val(ie->private_ext()->id(), TYPE_COUNT)); + ev->Assign(1, new StringVal(new BroString((const u_char*) d, len, 0))); + + return ev; + } + +Val* BuildCause(const InformationElement* ie) + { + return new Val(ie->cause()->value(), TYPE_COUNT); + } + +Val* BuildReorderReq(const InformationElement* ie) + { + return new Val(ie->reorder_req()->req(), TYPE_BOOL); + } + +Val* BuildChargingID(const InformationElement* ie) + { + return new Val(ie->charging_id()->value(), TYPE_COUNT);; + } + +Val* BuildChargingGatewayAddr(const InformationElement* ie) + { + const uint8* d = ie->charging_gateway_addr()->value().data(); + int len = ie->charging_gateway_addr()->value().length(); + if ( len == 4 ) + return new AddrVal(IPAddr(IPv4, (const uint32*) d, IPAddr::Network)); + else if ( len == 16 ) + return new AddrVal(IPAddr(IPv6, (const uint32*) d, IPAddr::Network)); + else + return 0; + } + +Val* BuildTeardownInd(const InformationElement* ie) + { + return new Val(ie->teardown_ind()->ind(), TYPE_BOOL); + } + +void CreatePDP_Request(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_create_pdp_ctx_request ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_create_pdp_ctx_request_elements); + + const vector * v = pdu->create_pdp_ctx_request(); + + bool second_nsapi = false; + bool second_gsn_addr = false; + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_IMSI: + rv->Assign(0, BuildIMSI(ie)); + break; + case GTPv1::TYPE_RAI: + rv->Assign(1, BuildRAI(ie)); + break; + case GTPv1::TYPE_RECOVERY: + rv->Assign(2, BuildRecovery(ie)); + break; + case GTPv1::TYPE_SELECTION_MODE: + rv->Assign(3, BuildSelectionMode(ie)); + break; + case GTPv1::TYPE_TEID1: + rv->Assign(4, BuildTEID1(ie)); + break; + case GTPv1::TYPE_TEID_CONTROL_PLANE: + rv->Assign(5, BuildTEID_ControlPlane(ie)); + break; + case GTPv1::TYPE_NSAPI: + if ( second_nsapi ) + rv->Assign(7, BuildNSAPI(ie)); + else + { + second_nsapi = true; + rv->Assign(6, BuildNSAPI(ie)); + } + break; + case GTPv1::TYPE_CHARGING_CHARACTERISTICS: + rv->Assign(8, BuildChargingCharacteristics(ie)); + break; + case GTPv1::TYPE_TRACE_REFERENCE: + rv->Assign(9, BuildTraceReference(ie)); + break; + case GTPv1::TYPE_TRACE_TYPE: + rv->Assign(10, BuildTraceType(ie)); + break; + case GTPv1::TYPE_END_USER_ADDR: + rv->Assign(11, BuildEndUserAddr(ie)); + break; + case GTPv1::TYPE_ACCESS_POINT_NAME: + rv->Assign(12, BuildAccessPointName(ie)); + break; + case GTPv1::TYPE_PROTO_CONFIG_OPTIONS: + rv->Assign(13, BuildProtoConfigOptions(ie)); + break; + case GTPv1::TYPE_GSN_ADDR: + if ( second_gsn_addr ) + rv->Assign(15, BuildGSN_Addr(ie)); + else + { + second_gsn_addr = true; + rv->Assign(14, BuildGSN_Addr(ie)); + } + break; + case GTPv1::TYPE_MSISDN: + rv->Assign(16, BuildMSISDN(ie)); + break; + case GTPv1::TYPE_QOS_PROFILE: + rv->Assign(17, BuildQoS_Profile(ie)); + break; + case GTPv1::TYPE_TRAFFIC_FLOW_TEMPLATE: + rv->Assign(18, BuildTrafficFlowTemplate(ie)); + break; + case GTPv1::TYPE_TRIGGER_ID: + rv->Assign(19, BuildTriggerID(ie)); + break; + case GTPv1::TYPE_OMC_ID: + rv->Assign(20, BuildOMC_ID(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(21, BuildPrivateExt(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_create_pdp_ctx_request(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } + +void CreatePDP_Response(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_create_pdp_ctx_response ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_create_pdp_ctx_response_elements); + + const vector * v = pdu->create_pdp_ctx_response(); + + bool second_gsn_addr = false; + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_CAUSE: + rv->Assign(0, BuildCause(ie)); + break; + case GTPv1::TYPE_REORDER_REQ: + rv->Assign(1, BuildReorderReq(ie)); + break; + case GTPv1::TYPE_RECOVERY: + rv->Assign(2, BuildRecovery(ie)); + break; + case GTPv1::TYPE_TEID1: + rv->Assign(3, BuildTEID1(ie)); + break; + case GTPv1::TYPE_TEID_CONTROL_PLANE: + rv->Assign(4, BuildTEID_ControlPlane(ie)); + break; + case GTPv1::TYPE_CHARGING_ID: + rv->Assign(5, BuildChargingID(ie)); + break; + case GTPv1::TYPE_END_USER_ADDR: + rv->Assign(6, BuildEndUserAddr(ie)); + break; + case GTPv1::TYPE_PROTO_CONFIG_OPTIONS: + rv->Assign(7, BuildProtoConfigOptions(ie)); + break; + case GTPv1::TYPE_GSN_ADDR: + if ( second_gsn_addr ) + rv->Assign(9, BuildGSN_Addr(ie)); + else + { + second_gsn_addr = true; + rv->Assign(8, BuildGSN_Addr(ie)); + } + break; + case GTPv1::TYPE_QOS_PROFILE: + rv->Assign(10, BuildQoS_Profile(ie)); + break; + case GTPv1::TYPE_CHARGING_GATEWAY_ADDR: + rv->Assign(11, BuildChargingGatewayAddr(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(12, BuildPrivateExt(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_create_pdp_ctx_response(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } + +void UpdatePDP_Request(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_update_pdp_ctx_request ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_update_pdp_ctx_request_elements); + + const vector * v = pdu->update_pdp_ctx_request(); + + bool second_gsn_addr = false; + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_IMSI: + rv->Assign(0, BuildIMSI(ie)); + break; + case GTPv1::TYPE_RAI: + rv->Assign(1, BuildRAI(ie)); + break; + case GTPv1::TYPE_RECOVERY: + rv->Assign(2, BuildRecovery(ie)); + break; + case GTPv1::TYPE_TEID1: + rv->Assign(3, BuildTEID1(ie)); + break; + case GTPv1::TYPE_TEID_CONTROL_PLANE: + rv->Assign(4, BuildTEID_ControlPlane(ie)); + break; + case GTPv1::TYPE_NSAPI: + rv->Assign(5, BuildNSAPI(ie)); + break; + case GTPv1::TYPE_TRACE_REFERENCE: + rv->Assign(6, BuildTraceReference(ie)); + break; + case GTPv1::TYPE_TRACE_TYPE: + rv->Assign(7, BuildTraceType(ie)); + break; + case GTPv1::TYPE_GSN_ADDR: + if ( second_gsn_addr ) + rv->Assign(9, BuildGSN_Addr(ie)); + else + { + second_gsn_addr = true; + rv->Assign(8, BuildGSN_Addr(ie)); + } + break; + case GTPv1::TYPE_QOS_PROFILE: + rv->Assign(10, BuildQoS_Profile(ie)); + break; + case GTPv1::TYPE_TRAFFIC_FLOW_TEMPLATE: + rv->Assign(11, BuildTrafficFlowTemplate(ie)); + break; + case GTPv1::TYPE_TRIGGER_ID: + rv->Assign(12, BuildTriggerID(ie)); + break; + case GTPv1::TYPE_OMC_ID: + rv->Assign(13, BuildOMC_ID(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(14, BuildPrivateExt(ie)); + break; + case GTPv1::TYPE_END_USER_ADDR: + rv->Assign(15, BuildEndUserAddr(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_update_pdp_ctx_request(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } + +void UpdatePDP_Response(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_update_pdp_ctx_response ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_update_pdp_ctx_response_elements); + + const vector * v = pdu->update_pdp_ctx_response(); + + bool second_gsn_addr = false; + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_CAUSE: + rv->Assign(0, BuildCause(ie)); + break; + case GTPv1::TYPE_RECOVERY: + rv->Assign(1, BuildRecovery(ie)); + break; + case GTPv1::TYPE_TEID1: + rv->Assign(2, BuildTEID1(ie)); + break; + case GTPv1::TYPE_TEID_CONTROL_PLANE: + rv->Assign(3, BuildTEID_ControlPlane(ie)); + break; + case GTPv1::TYPE_CHARGING_ID: + rv->Assign(4, BuildChargingID(ie)); + break; + case GTPv1::TYPE_GSN_ADDR: + if ( second_gsn_addr ) + rv->Assign(6, BuildGSN_Addr(ie)); + else + { + second_gsn_addr = true; + rv->Assign(5, BuildGSN_Addr(ie)); + } + break; + case GTPv1::TYPE_QOS_PROFILE: + rv->Assign(7, BuildQoS_Profile(ie)); + break; + case GTPv1::TYPE_CHARGING_GATEWAY_ADDR: + rv->Assign(8, BuildChargingGatewayAddr(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(9, BuildPrivateExt(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_update_pdp_ctx_response(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } + +void DeletePDP_Request(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_delete_pdp_ctx_request ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_delete_pdp_ctx_request_elements); + + const vector * v = pdu->delete_pdp_ctx_request(); + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_TEARDOWN_IND: + rv->Assign(0, BuildTeardownInd(ie)); + break; + case GTPv1::TYPE_NSAPI: + rv->Assign(1, BuildNSAPI(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(2, BuildPrivateExt(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_delete_pdp_ctx_request(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } + +void DeletePDP_Response(const BroAnalyzer& a, const GTPv1_Header* pdu) + { + if ( ! ::gtpv1_delete_pdp_ctx_response ) return; + + RecordVal* rv = new RecordVal( + BifType::Record::gtp_delete_pdp_ctx_response_elements); + + const vector * v = pdu->delete_pdp_ctx_response(); + + for ( size_t i = 0; i < v->size(); ++i ) + { + const InformationElement* ie = (*v)[i]; + + switch ( ie->type() ) { + case GTPv1::TYPE_CAUSE: + rv->Assign(0, BuildCause(ie)); + break; + case GTPv1::TYPE_PRIVATE_EXT: + rv->Assign(1, BuildPrivateExt(ie)); + break; + default: + a->Weird(fmt("gtp_invalid_info_element_%d", (*v)[i]->type())); + break; + } + } + + BifEvent::generate_gtpv1_delete_pdp_ctx_response(a, a->Conn(), + BuildGTPv1Hdr(pdu), rv); + } +%} + connection GTPv1_Conn(bro_analyzer: BroAnalyzer) { upflow = GTPv1_Flow(true); @@ -27,17 +625,17 @@ connection GTPv1_Conn(bro_analyzer: BroAnalyzer) %} } -%code{ -inline void violate(const char* r, const BroAnalyzer& a, const bytestring& p) - { - a->ProtocolViolation(r, (const char*) p.data(), p.length()); - } -%} - flow GTPv1_Flow(is_orig: bool) { datagram = GTPv1_Header withcontext(connection, this); + function violate(r: string, pdu: GTPv1_Header): void + %{ + BroAnalyzer a = connection()->bro_analyzer(); + const_bytestring b = ${pdu.sourcedata}; + a->ProtocolViolation(r.c_str(), (const char*) b.begin(), b.length()); + %} + function process_gtpv1(pdu: GTPv1_Header): bool %{ BroAnalyzer a = connection()->bro_analyzer(); @@ -55,14 +653,14 @@ flow GTPv1_Flow(is_orig: bool) if ( e && e->LastType() == BifEnum::Tunnel::GTPv1 ) { // GTP is never tunneled in GTP so, this must be a regular packet - violate("GTP-in-GTP", a, ${pdu.packet}); + violate("GTP-in-GTP", pdu); return false; } if ( ${pdu.version} != 1 ) { // Only know of GTPv1 with Version == 1 - violate("GTPv1 bad Version", a, ${pdu.packet}); + violate("GTPv1 bad Version", pdu); return false; } @@ -72,21 +670,46 @@ flow GTPv1_Flow(is_orig: bool) return false; } - if ( ${pdu.e_flag} ) - { - // TODO: can't currently parse past extension headers - return false; - } + if ( ::gtpv1_message ) + BifEvent::generate_gtpv1_message(a, c, BuildGTPv1Hdr(pdu)); - if ( ${pdu.msg_type} != 0xff ) - { - // Only interested in decapsulating user plane data beyond here. + switch ( ${pdu.msg_type} ) { + case 16: + CreatePDP_Request(a, pdu); + return true; + case 17: + CreatePDP_Response(a, pdu); + return true; + case 18: + UpdatePDP_Request(a, pdu); + return true; + case 19: + UpdatePDP_Response(a, pdu); + return true; + case 20: + DeletePDP_Request(a, pdu); + return true; + case 21: + DeletePDP_Response(a, pdu); + return true; + case 255: + return process_g_pdu(pdu); + default: return false; - } + } + + return false; + %} + + function process_g_pdu(pdu: GTPv1_Header): bool + %{ + BroAnalyzer a = connection()->bro_analyzer(); + Connection *c = a->Conn(); + const EncapsulationStack* e = c->GetEncapsulation(); if ( ${pdu.packet}.length() < (int)sizeof(struct ip) ) { - violate("Truncated GTPv1", a, ${pdu.packet}); + violate("Truncated GTPv1", pdu); return false; } @@ -94,7 +717,7 @@ flow GTPv1_Flow(is_orig: bool) if ( ip->ip_v != 4 && ip->ip_v != 6 ) { - violate("non-IP packet in GTPv1", a, ${pdu.packet}); + violate("non-IP packet in GTPv1", pdu); return false; } @@ -113,10 +736,10 @@ flow GTPv1_Flow(is_orig: bool) } else if ( result < 0 ) - violate("Truncated GTPv1", a, ${pdu.packet}); + violate("Truncated GTPv1", pdu); else - violate("GTPv1 payload length", a, ${pdu.packet}); + violate("GTPv1 payload length", pdu); if ( result != 0 ) { @@ -125,37 +748,16 @@ flow GTPv1_Flow(is_orig: bool) } if ( ::gtpv1_g_pdu_packet ) - { - RecordVal* rv = new RecordVal(gtpv1_hdr_type); - - rv->Assign(0, new Val(${pdu.version}, TYPE_COUNT)); - rv->Assign(1, new Val(${pdu.pt_flag}, TYPE_BOOL)); - rv->Assign(2, new Val(${pdu.rsv}, TYPE_BOOL)); - rv->Assign(3, new Val(${pdu.e_flag}, TYPE_BOOL)); - rv->Assign(4, new Val(${pdu.s_flag}, TYPE_BOOL)); - rv->Assign(5, new Val(${pdu.pn_flag}, TYPE_BOOL)); - rv->Assign(6, new Val(${pdu.msg_type}, TYPE_COUNT)); - rv->Assign(7, new Val(ntohs(${pdu.length}), TYPE_COUNT)); - rv->Assign(8, new Val(ntohl(${pdu.teid}), TYPE_COUNT)); - - if ( ${pdu.has_opt} ) - { - rv->Assign(9, new Val(ntohs(${pdu.opt_hdr.seq}), TYPE_COUNT)); - rv->Assign(10, new Val(${pdu.opt_hdr.n_pdu}, TYPE_COUNT)); - rv->Assign(11, new Val(${pdu.opt_hdr.next_type}, TYPE_COUNT)); - } - - BifEvent::generate_gtpv1_g_pdu_packet(a, c, rv, + BifEvent::generate_gtpv1_g_pdu_packet(a, c, BuildGTPv1Hdr(pdu), inner->BuildPktHdrVal()); - } EncapsulatingConn ec(c, BifEnum::Tunnel::GTPv1); sessions->DoNextInnerPacket(network_time(), 0, inner, e, ec); - return (result == 0) ? true : false; + return true; %} - }; refine typeattr GTPv1_Header += &let { proc_gtpv1 = $context.flow.process_gtpv1(this); }; + diff --git a/src/gtpv1-protocol.pac b/src/gtpv1-protocol.pac index 5bf31a48ee..0a87665fd2 100644 --- a/src/gtpv1-protocol.pac +++ b/src/gtpv1-protocol.pac @@ -4,11 +4,27 @@ type GTPv1_Header = record { msg_type: uint8; length: uint16; teid: uint32; + opt: case has_opt of { true -> opt_hdr: GTPv1_Opt_Header; false -> no_opt: empty; - } &requires(has_opt); - packet: bytestring &restofdata; + }; + + ext: case e_flag of { + true -> ext_hdrs: GTPv1_Ext_Header[] &until($element.next_type == 0); + false -> no_ext: empty; + }; + + msg: case msg_type of { + 16 -> create_pdp_ctx_request: InformationElement[]; + 17 -> create_pdp_ctx_response: InformationElement[]; + 18 -> update_pdp_ctx_request: InformationElement[]; + 19 -> update_pdp_ctx_response: InformationElement[]; + 20 -> delete_pdp_ctx_request: InformationElement[]; + 21 -> delete_pdp_ctx_response: InformationElement[]; + 255 -> packet: bytestring &restofdata; + default -> unknown: bytestring &restofdata; + }; } &let { version: uint8 = (flags & 0xE0) >> 5; @@ -18,10 +34,463 @@ type GTPv1_Header = record { s_flag: bool = flags & 0x02; pn_flag: bool = flags & 0x01; has_opt: bool = flags & 0x07; -} &byteorder = littleendian; +} &byteorder = bigendian, &exportsourcedata; type GTPv1_Opt_Header = record { seq: uint16; n_pdu: uint8; next_type: uint8; -} +}; + +type GTPv1_Ext_Header = record { + length: uint8; + contents: bytestring &length=(length * 4 - 2); + next_type: uint8; +}; + +enum InfoElementType { + TYPE_CAUSE = 1, + TYPE_IMSI = 2, + TYPE_RAI = 3, + TYPE_TLLI = 4, + TYPE_P_TMSI = 5, + TYPE_REORDER_REQ = 8, + TYPE_AUTHN_TRIPLET = 9, + TYPE_MAP_CAUSE = 11, + TYPE_P_TMSI_SIG = 12, + TYPE_MS_VALID = 13, + TYPE_RECOVERY = 14, + TYPE_SELECTION_MODE = 15, + TYPE_TEID1 = 16, + TYPE_TEID_CONTROL_PLANE = 17, + TYPE_TEID2 = 18, + TYPE_TEARDOWN_IND = 19, + TYPE_NSAPI = 20, + TYPE_RANAP_CAUSE = 21, + TYPE_RAB_CTX = 22, + TYPE_RADIO_PRIORITY_SMS = 23, + TYPE_RADIO_PRIORITY = 24, + TYPE_PACKET_FLOW_ID = 25, + TYPE_CHARGING_CHARACTERISTICS = 26, + TYPE_TRACE_REFERENCE = 27, + TYPE_TRACE_TYPE = 28, + TYPE_MS_NOT_REACHABLE_REASON = 29, + TYPE_CHARGING_ID = 127, + TYPE_END_USER_ADDR = 128, + TYPE_MM_CTX = 129, + TYPE_PDP_CTX = 130, + TYPE_ACCESS_POINT_NAME = 131, + TYPE_PROTO_CONFIG_OPTIONS = 132, + TYPE_GSN_ADDR = 133, + TYPE_MSISDN = 134, + TYPE_QOS_PROFILE = 135, + TYPE_AUTHN_QUINTUPLET = 136, + TYPE_TRAFFIC_FLOW_TEMPLATE = 137, + TYPE_TARGET_ID = 138, + TYPE_UTRAN_TRANSPARENT_CONTAINER = 139, + TYPE_RAB_SETUP_INFO = 140, + TYPE_EXT_HEADER_TYPE_LIST = 141, + TYPE_TRIGGER_ID = 142, + TYPE_OMC_ID = 143, + TYPE_CHARGING_GATEWAY_ADDR = 251, + TYPE_PRIVATE_EXT = 255, +}; + +type InformationElement = record { + type: uint8; + + len: case is_tlv of { + true -> tlv_len: uint16; + false -> no_len: empty; + }; + + value: case type of { + TYPE_CAUSE -> cause: Cause; + TYPE_IMSI -> imsi: IMSI; + TYPE_RAI -> rai: RAI; + TYPE_TLLI -> tlli: TLLI; + TYPE_P_TMSI -> p_tmsi: P_TMSI; + TYPE_REORDER_REQ -> reorder_req: ReorderReq; + TYPE_AUTHN_TRIPLET -> authn_triplet: AuthN_Triplet; + TYPE_MAP_CAUSE -> map_cause: MAP_Cause; + TYPE_P_TMSI_SIG -> p_tmsi_sig: P_TMSI_Sig; + TYPE_MS_VALID -> ms_valid: MS_Valid; + TYPE_RECOVERY -> recovery: Recovery; + TYPE_SELECTION_MODE -> selection_mode: SelectionMode; + TYPE_TEID1 -> teid1: TEID1; + TYPE_TEID_CONTROL_PLANE -> teidcp: TEID_ControlPlane; + TYPE_TEID2 -> teid2: TEID2; + TYPE_TEARDOWN_IND -> teardown_ind: TeardownInd; + TYPE_NSAPI -> nsapi: NSAPI; + TYPE_RANAP_CAUSE -> ranap_cause: RANAP_Cause; + TYPE_RAB_CTX -> rab_ctx: RAB_Ctx; + TYPE_RADIO_PRIORITY_SMS -> radio_priority_sms: RadioPrioritySMS; + TYPE_RADIO_PRIORITY -> radio_priority: RadioPriority; + TYPE_PACKET_FLOW_ID -> packet_flow_id: PacketFlowID; + TYPE_CHARGING_CHARACTERISTICS -> charging_characteristics: ChargingCharacteristics; + TYPE_TRACE_REFERENCE -> trace_reference: TraceReference; + TYPE_TRACE_TYPE -> trace_type: TraceType; + TYPE_MS_NOT_REACHABLE_REASON -> ms_not_reachable_reason: MS_Not_Reachable_Reason; + TYPE_CHARGING_ID -> charging_id: ChargingID; + TYPE_END_USER_ADDR -> end_user_addr: EndUserAddr(length); + TYPE_MM_CTX -> mm_ctx: MM_Ctx(length); + TYPE_PDP_CTX -> pdp_ctx: PDP_Ctx(length); + TYPE_ACCESS_POINT_NAME -> ap_name: AP_Name(length); + TYPE_PROTO_CONFIG_OPTIONS -> proto_config_opts: ProtoConfigOpts(length); + TYPE_GSN_ADDR -> gsn_addr: GSN_Addr(length); + TYPE_MSISDN -> msisdn: MSISDN(length); + TYPE_QOS_PROFILE -> qos_profile: QoS_Profile(length); + TYPE_AUTHN_QUINTUPLET -> authn_quintuplet: AuthN_Quintuplet(length); + TYPE_TRAFFIC_FLOW_TEMPLATE -> traffic_flow_template: TrafficFlowTemplate(length); + TYPE_TARGET_ID -> target_id: TargetID(length); + TYPE_UTRAN_TRANSPARENT_CONTAINER -> utran_transparent_container: UTRAN_TransparentContainer(length); + TYPE_RAB_SETUP_INFO -> rab_setup_info: RAB_SetupInfo(length); + TYPE_EXT_HEADER_TYPE_LIST -> ext_hdr_type_list: ExtHdrTypeList(length); + TYPE_TRIGGER_ID -> trigger_id: TriggerID(length); + TYPE_OMC_ID -> omc_id: OMC_ID(length); + TYPE_CHARGING_GATEWAY_ADDR -> charging_gateway_addr: ChargingGatewayAddr(length); + TYPE_PRIVATE_EXT -> private_ext: PrivateExt(length); + default -> unknown: bytestring &length=length; + } &requires(length); + +} &let { + is_tlv: bool = (type & 0x80); + length: uint16 = is_tlv ? tlv_len : Get_IE_Len(type); +}; + +type Cause = record { + value: uint8; +}; + +function decode_imsi(v: uint8[8]): uint64 + %{ + uint64 rval = 0; + uint8 digits[16]; + for ( size_t i = 0; i < v->size(); ++i ) + { + digits[2 * i + 1] = ((*v)[i] & 0xf0) >> 4; + digits[2 * i] = (*v)[i] & 0x0f; + } + int power = 0; + for ( int i = 15; i >= 0; --i ) + { + if ( digits[i] == 0x0f ) continue; + rval += digits[i] * pow(10, power); + ++power; + } + return rval; + %} + +type IMSI = record { + tbcd_encoded_value: uint8[8]; +} &let { + value: uint64 = decode_imsi(tbcd_encoded_value); +}; + +type RAI = record { + mcc2_mcc1: uint8; + mnc3_mcc3: uint8; + mnc2_mnc1: uint8; + lac: uint16; + rac: uint8; +} &let { + mcc1: uint8 = (mcc2_mcc1 & 0x0f); + mcc2: uint8 = ((mcc2_mcc1 & 0xf0)>>4); + mcc3: uint8 = (mnc3_mcc3 & 0x0f); + mcc: uint16 = mcc1 * 100 + mcc2 * 10 + mcc3; + mnc1: uint8 = (mnc2_mnc1 & 0x0f); + mnc2: uint8 = ((mnc2_mnc1 & 0xf0)>>4); + mnc3: uint8 = (mnc3_mcc3 & 0xf0)>>4; + mnc: uint16 = (mnc3 & 0x0f) ? mnc1 * 10 + mnc2 : mnc1 * 100 + mnc2 * 10 + mnc3; +}; + +type TLLI = record { + value: uint32; +}; + +type P_TMSI = record { + value: uint32; +}; + +type ReorderReq = record { + value: uint8; +} &let { + req: bool = value & 0x01; +}; + +type AuthN_Triplet = record { + rand: bytestring &length=16; + sres: uint32; + kc: uint64; +}; + +type MAP_Cause = record { + value: uint8; +}; + +type P_TMSI_Sig = record { + value: bytestring &length=3; +}; + +type MS_Valid = record { + value: uint8; +}; + +type Recovery = record { + restart_counter: uint8; +}; + +type SelectionMode = record { + value: uint8; +} &let { + mode: uint8 = value & 0x01; +}; + +type TEID1 = record { + value: uint32; +}; + +type TEID_ControlPlane = record { + value: uint32; +}; + +type TEID2 = record { + spare_nsapi: uint8; + teid2: uint32; +}; + +type TeardownInd = record { + value: uint8; +} &let { + ind: bool = value & 0x01; +}; + +type NSAPI = record { + xxxx_nsapi: uint8; +} &let { + nsapi: uint8 = xxxx_nsapi & 0x0f; +}; + +type RANAP_Cause = record { + value: uint8; +}; + +type RAB_Ctx = record { + spare_nsapi: uint8; + dl_gtpu_seq_num: uint16; + ul_gtpu_seq_num: uint16; + dl_pdcp_seq_num: uint16; + ul_pdcp_seq_num: uint16; +}; + +type RadioPrioritySMS = record { + value: uint8; +}; + +type RadioPriority = record { + nsapi_radio_priority: uint8; +}; + +type PacketFlowID = record { + rsv_nsapi: uint8; + packet_flow_id: uint8; +}; + +type ChargingCharacteristics = record { + value: uint16; +}; + +type TraceReference = record { + value: uint16; +}; + +type TraceType = record { + value: uint16; +}; + +type MS_Not_Reachable_Reason = record { + value: uint8; +}; + +type ChargingID = record { + value: uint32; +}; + +type EndUserAddr(n: uint16) = record { + spare_pdp_type_org: uint8; + pdp_type_num: uint8; + pdp_addr: bytestring &length=(n-2); +} &let { + pdp_type_org: uint8 = spare_pdp_type_org & 0x0f; +}; + +type MM_Ctx(n: uint16) = record { + spare_cksn_ksi: uint8; + security_params: uint8; + + keys: case gsm_keys of { + true -> kc: uint64; + false -> ck_ik: bytestring &length=32; + }; + + vector_len: case have_triplets of { + true -> no_quint_len: empty; + false -> quint_len: uint16; + }; + + vectors: case have_triplets of { + true -> triplets: AuthN_Triplet[num_vectors]; + false -> quintuplets: AuthN_Quintuplet(quint_len)[num_vectors]; + } &requires(num_vectors); + + drx_param: uint16; + ms_net_capability_len: uint8; + ms_net_capability: bytestring &length=ms_net_capability_len; + container_len: uint16; + container: bytestring &length=container_len; + +} &let { + security_mode: uint8 = security_params >> 6; + gsm_keys: bool = security_mode & 0x01; + have_triplets: bool = (security_mode == 1); + num_vectors: uint8 = (security_params & 0x38) >> 3; +}; + +type PDP_Ctx(n: uint16) = record { + rsv_nsapi: uint8; + xxxx_sapi: uint8; + qos_sub_len: uint8; + qos_sub: QoS_Profile(qos_sub_len); + qos_req_len: uint8; + qos_req: QoS_Profile(qos_req_len); + qos_neg_len: uint8; + qos_neg: QoS_Profile(qos_neg_len); + snd: uint16; + snu: uint16; + send_npdu_num: uint8; + recv_npdu_num: uint8; + ul_teid_cp: TEID_ControlPlane; + ul_teid_data1: TEID1; + pdp_ctx_id: uint8; + spare_pdp_type_org: uint8; + pdp_type_num: uint8; + pdp_addr_len: uint8; + pdp_addr: bytestring &length=pdp_addr_len; + ggsn_addr_control_plane_len: uint8; + ggsn_addr_control_plane: bytestring &length=ggsn_addr_control_plane_len; + ggsn_addr_user_traffic_len: uint8; + ggsn_addr_user_traffic: bytestring &length=ggsn_addr_user_traffic_len; + apn_len: uint8; + apn: AP_Name(apn_len); + spare_transaction_id: uint8; + transaction_id: uint8; +}; + +type AP_Name(n: uint16) = record { + value: bytestring &length=n; +}; + +type ProtoConfigOpts(n: uint16) = record { + value: bytestring &length=n; +}; + +type GSN_Addr(n: uint16) = record { + value: bytestring &length=n; +}; + +type MSISDN(n: uint16) = record { + value: bytestring &length=n; +}; + +type QoS_Profile(n: uint16) = record { + alloc_retention_priority: uint8; + data: bytestring &length=n-1; +}; + +type AuthN_Quintuplet(n: uint16) = record { + rand: bytestring &length=16; + xres_len: uint8; + xres: bytestring &length=xres_len; + ck: bytestring &length=16; + ik: bytestring &length=16; + autn_len: uint8; + autn: bytestring &length=autn_len; +}; + +type TrafficFlowTemplate(n: uint16) = record { + value: bytestring &length=n; +}; + +type TargetID(n: uint16) = record { + value: bytestring &length=n; +}; + +type UTRAN_TransparentContainer(n: uint16) = record { + value: bytestring &length=n; +}; + +type RAB_SetupInfo(n: uint16) = record { + xxxx_nsapi: uint8; + + have_teid: case n of { + 1 -> no_teid: empty; + default -> teid: TEID1; + }; + + have_addr: case n of { + 1 -> no_addr: empty; + default -> rnc_addr: bytestring &length=n-5; + }; +}; + +type ExtHdrTypeList(n: uint16) = record { + value: uint8[n]; +}; + +type TriggerID(n: uint16) = record { + value: bytestring &length=n; +}; + +type OMC_ID(n: uint16) = record { + value: bytestring &length=n; +}; + +type ChargingGatewayAddr(n: uint16) = record { + value: bytestring &length=n; +}; + +type PrivateExt(n: uint16) = record { + id: uint16; + value: bytestring &length=n-2; +}; + +function Get_IE_Len(t: uint8): uint16 = + case t of { + TYPE_CAUSE -> 1; + TYPE_IMSI -> 8; + TYPE_RAI -> 6; + TYPE_TLLI -> 4; + TYPE_P_TMSI -> 4; + TYPE_REORDER_REQ -> 1; + TYPE_AUTHN_TRIPLET -> 28; + TYPE_MAP_CAUSE -> 1; + TYPE_P_TMSI_SIG -> 3; + TYPE_MS_VALID -> 1; + TYPE_RECOVERY -> 1; + TYPE_SELECTION_MODE -> 1; + TYPE_TEID1 -> 4; + TYPE_TEID_CONTROL_PLANE -> 4; + TYPE_TEID2 -> 5; + TYPE_TEARDOWN_IND -> 1; + TYPE_NSAPI -> 1; + TYPE_RANAP_CAUSE -> 1; + TYPE_RAB_CTX -> 9; + TYPE_RADIO_PRIORITY_SMS -> 1; + TYPE_RADIO_PRIORITY -> 1; + TYPE_PACKET_FLOW_ID -> 2; + TYPE_CHARGING_CHARACTERISTICS -> 2; + TYPE_TRACE_REFERENCE -> 2; + TYPE_TRACE_TYPE -> 2; + TYPE_MS_NOT_REACHABLE_REASON -> 1; + TYPE_CHARGING_ID -> 4; + }; diff --git a/src/types.bif b/src/types.bif index 888310419c..27009d2e1c 100644 --- a/src/types.bif +++ b/src/types.bif @@ -211,3 +211,17 @@ enum Mode %{ %} module GLOBAL; + +type gtpv1_hdr: record; +type gtp_create_pdp_ctx_request_elements: record; +type gtp_create_pdp_ctx_response_elements: record; +type gtp_update_pdp_ctx_request_elements: record; +type gtp_update_pdp_ctx_response_elements: record; +type gtp_delete_pdp_ctx_request_elements: record; +type gtp_delete_pdp_ctx_response_elements: record; + +type gtp_end_user_addr: record; +type gtp_rai: record; +type gtp_qos_profile: record; +type gtp_private_extension: record; +type gtp_gsn_addr: record; diff --git a/testing/btest/Baseline/core.tunnels.gtp.ext_header/out b/testing/btest/Baseline/core.tunnels.gtp.ext_header/out new file mode 100644 index 0000000000..e76a7f35de --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gtp.ext_header/out @@ -0,0 +1,2 @@ +gtpv1_message, [orig_h=10.155.148.149, orig_p=9000/udp, resp_h=10.155.148.157, resp_p=2152/udp] +[version=1, pt_flag=T, rsv=F, e_flag=T, s_flag=T, pn_flag=F, msg_type=255, length=1508, teid=1050199, seq=5, n_pdu=0, next_type=192] diff --git a/testing/btest/Baseline/core.tunnels.gtp.non_recursive/out b/testing/btest/Baseline/core.tunnels.gtp.non_recursive/out index a299c4d592..49bb5f7399 100644 --- a/testing/btest/Baseline/core.tunnels.gtp.non_recursive/out +++ b/testing/btest/Baseline/core.tunnels.gtp.non_recursive/out @@ -1 +1 @@ -protocol_violation, [orig_h=74.125.216.149, orig_p=2152/udp, resp_h=10.131.138.69, resp_p=2152/udp], GTP-in-GTP [n\xd9'|\x00\x00\x01\xb6[\xf6\xdc0\xb7d\xe5\xe6\xa76\x91\xfbk\x0e\x02\xc8A\x05\xa8\xe6\xf3Gi\x80(]\xcew\x84\xae}\xd2...] +protocol_violation, [orig_h=74.125.216.149, orig_p=2152/udp, resp_h=10.131.138.69, resp_p=2152/udp], GTP-in-GTP [\x80\xe1Bc.\xe20\xebn\xd9'|\x00\x00\x01\xb6[\xf6\xdc0\xb7d\xe5\xe6\xa76\x91\xfbk\x0e\x02\xc8A\x05\xa8\xe6\xf3Gi\x80...] diff --git a/testing/btest/Baseline/core.tunnels.gtp.pdp_ctx_messages/out b/testing/btest/Baseline/core.tunnels.gtp.pdp_ctx_messages/out new file mode 100644 index 0000000000..fcdfe94824 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gtp.pdp_ctx_messages/out @@ -0,0 +1,24 @@ +gtpv1_message, [orig_h=192.169.100.1, orig_p=34273/udp, resp_h=10.100.200.33, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=16, length=137, teid=0, seq=4875, n_pdu=0, next_type=0] +gtp create request, [orig_h=192.169.100.1, orig_p=34273/udp, resp_h=10.100.200.33, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=16, length=137, teid=0, seq=4875, n_pdu=0, next_type=0] +[imsi=460004100000101, rai=[mcc=460, mnc=6, lac=65534, rac=255], recovery=176, select_mode=1, data1=854600697, cp=854600697, nsapi=5, linked_nsapi=, charge_character=, trace_ref=, trace_type=, end_user_addr=[pdp_type_org=1, pdp_type_num=33, pdp_ip=, pdp_other_addr=], ap_name=^Feetest, opts=\x80\x80!^V^A^A\0^V^C^F\0\0\0\0\x81^F\0\0\0\0\x83^F\0\0\0\0, signal_addr=[ip=192.169.100.1, other=], user_addr=[ip=192.169.100.1, other=], msisdn=\x91hQ"^A\0^A\xf1, qos_prof=[priority=2, data=\x1bB\x1fs\x8c@@tK@@], tft=, trigger_id=, omc_id=, ext=[id=10923, value=^B^A^C]] +gtpv1_message, [orig_h=192.169.100.1, orig_p=34273/udp, resp_h=10.100.200.33, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=17, length=101, teid=854600697, seq=4875, n_pdu=0, next_type=0] +gtp create response, [orig_h=192.169.100.1, orig_p=34273/udp, resp_h=10.100.200.33, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=17, length=101, teid=854600697, seq=4875, n_pdu=0, next_type=0] +[cause=128, reorder_req=F, recovery=24, data1=268435589, cp=268435584, charging_id=103000009, end_user_addr=[pdp_type_org=1, pdp_type_num=33, pdp_ip=192.168.252.130, pdp_other_addr=], opts=\x80\x80!^P^D^A\0^P\x81^F\0\0\0\0\x83^F\0\0\0\0\x80!^J^C^A\0^J^C^F\xc0\xa8\xfc\x82, cp_addr=[ip=10.100.200.34, other=], user_addr=[ip=10.100.200.49, other=], qos_prof=[priority=2, data=\x1bB\x1fs\x8c@@tK@@], charge_gateway=, ext=] +gtpv1_message, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=1, length=4, teid=0, seq=3072, n_pdu=0, next_type=0] +gtpv1_message, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=2, length=6, teid=0, seq=3072, n_pdu=0, next_type=0] +gtpv1_message, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=16, length=104, teid=0, seq=3073, n_pdu=0, next_type=0] +gtp create request, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=16, length=104, teid=0, seq=3073, n_pdu=0, next_type=0] +[imsi=240010123456789, rai=, recovery=3, select_mode=1, data1=1, cp=1, nsapi=0, linked_nsapi=, charge_character=2048, trace_ref=, trace_type=, end_user_addr=[pdp_type_org=1, pdp_type_num=33, pdp_ip=, pdp_other_addr=], ap_name=^Hinternet, opts=\x80\xc0#^Q^A^A\0^Q^Cmig^Hhemmelig, signal_addr=[ip=127.0.0.2, other=], user_addr=[ip=127.0.0.2, other=], msisdn=\x91d^G^R2T\xf6, qos_prof=[priority=0, data=^K\x92\x1f], tft=, trigger_id=, omc_id=, ext=] +gtpv1_message, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=17, length=78, teid=1, seq=3073, n_pdu=0, next_type=0] +gtp create response, [orig_h=127.0.0.2, orig_p=2123/udp, resp_h=127.0.0.1, resp_p=2123/udp] +[version=1, pt_flag=T, rsv=F, e_flag=F, s_flag=T, pn_flag=F, msg_type=17, length=78, teid=1, seq=3073, n_pdu=0, next_type=0] +[cause=128, reorder_req=F, recovery=1, data1=1, cp=1, charging_id=1, end_user_addr=[pdp_type_org=1, pdp_type_num=33, pdp_ip=192.168.0.2, pdp_other_addr=], opts=\x80\x80!^P^B\0\0^P\x81^F\0\0\0\0\x83^F\0\0\0\0, cp_addr=[ip=127.0.0.1, other=], user_addr=[ip=127.0.0.1, other=], qos_prof=[priority=0, data=^K\x92\x1f], charge_gateway=, ext=] diff --git a/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/dpd.log b/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/dpd.log index 221fa16f4f..fcd110f8ab 100644 --- a/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/dpd.log +++ b/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/dpd.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path dpd -#open 2012-10-19-17-38-54 +#open 2013-01-25-21-49-19 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto analyzer failure_reason #types time string addr port addr port enum string string -1333458853.075889 UWkUyAuUGXf 173.86.159.28 2152 213.72.147.186 2152 udp GTPV1 Truncated GTPv1 [E\x00\x05\xc8G\xea@\x00\x80\x06\xb6\x83\x0a\x83w&\xd9\x14\x9c\x04\xd9\xc2\x00P\xddh\xb4\x8f41eVP\x10\x10\xe0u\xcf\x00\x00...] -#close 2012-10-19-17-38-54 +1333458853.075889 UWkUyAuUGXf 173.86.159.28 2152 213.72.147.186 2152 udp GTPV1 Truncated GTPv1 [0\xff\x00\xac\x98\x13\x01LE\x00\x05\xc8G\xea@\x00\x80\x06\xb6\x83\x0a\x83w&\xd9\x14\x9c\x04\xd9\xc2\x00P\xddh\xb4\x8f41eV...] +#close 2013-01-25-21-49-19 diff --git a/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/tunnel.log b/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/tunnel.log index 659090a581..070a754702 100644 --- a/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/tunnel.log +++ b/testing/btest/Baseline/core.tunnels.gtp.unknown_or_too_short/tunnel.log @@ -3,9 +3,9 @@ #empty_field (empty) #unset_field - #path tunnel -#open 2012-10-19-17-38-54 +#open 2013-01-25-21-49-19 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action #types time string addr port addr port enum enum 1333458853.034734 UWkUyAuUGXf 173.86.159.28 2152 213.72.147.186 2152 Tunnel::GTPv1 Tunnel::DISCOVER 1333458853.108391 UWkUyAuUGXf 173.86.159.28 2152 213.72.147.186 2152 Tunnel::GTPv1 Tunnel::CLOSE -#close 2012-10-19-17-38-54 +#close 2013-01-25-21-49-19 diff --git a/testing/btest/Traces/tunnels/gtp/gtp_control_prime.pcap b/testing/btest/Traces/tunnels/gtp/gtp_control_prime.pcap new file mode 100644 index 0000000000..ebed358a26 Binary files /dev/null and b/testing/btest/Traces/tunnels/gtp/gtp_control_prime.pcap differ diff --git a/testing/btest/Traces/tunnels/gtp/gtp_create_pdp_ctx.pcap b/testing/btest/Traces/tunnels/gtp/gtp_create_pdp_ctx.pcap new file mode 100644 index 0000000000..78283156d5 Binary files /dev/null and b/testing/btest/Traces/tunnels/gtp/gtp_create_pdp_ctx.pcap differ diff --git a/testing/btest/Traces/tunnels/gtp/gtp_ext_header.pcap b/testing/btest/Traces/tunnels/gtp/gtp_ext_header.pcap new file mode 100644 index 0000000000..453ebf934a Binary files /dev/null and b/testing/btest/Traces/tunnels/gtp/gtp_ext_header.pcap differ diff --git a/testing/btest/core/tunnels/gtp/ext_header.test b/testing/btest/core/tunnels/gtp/ext_header.test new file mode 100644 index 0000000000..6316acb184 --- /dev/null +++ b/testing/btest/core/tunnels/gtp/ext_header.test @@ -0,0 +1,8 @@ +# @TEST-EXEC: bro -r $TRACES/tunnels/gtp/gtp_ext_header.pcap %INPUT >out +# @TEST-EXEC: btest-diff out + +event gtpv1_message(c: connection, hdr: gtpv1_hdr) + { + print "gtpv1_message", c$id; + print hdr; + } diff --git a/testing/btest/core/tunnels/gtp/pdp_ctx_messages.test b/testing/btest/core/tunnels/gtp/pdp_ctx_messages.test new file mode 100644 index 0000000000..7405c8d019 --- /dev/null +++ b/testing/btest/core/tunnels/gtp/pdp_ctx_messages.test @@ -0,0 +1,56 @@ +# @TEST-EXEC: bro -r $TRACES/tunnels/gtp/gtp_control_prime.pcap -r $TRACES/tunnels/gtp/gtp_create_pdp_ctx.pcap %INPUT >out +# @TEST-EXEC: btest-diff out + +event gtpv1_message(c: connection, hdr: gtpv1_hdr) + { + print "gtpv1_message", c$id; + print hdr; + } + +event gtpv1_create_pdp_ctx_request(c: connection, hdr: gtpv1_hdr, + elements: gtp_create_pdp_ctx_request_elements) + { + print "gtp create request", c$id; + print hdr; + print elements; + } + +event gtpv1_create_pdp_ctx_response(c: connection, hdr: gtpv1_hdr, + elements: gtp_create_pdp_ctx_response_elements) + { + print "gtp create response", c$id; + print hdr; + print elements; + } + +event gtpv1_update_pdp_ctx_request(c: connection, hdr: gtpv1_hdr, + elements: gtp_update_pdp_ctx_request_elements) + { + print "gtp update request", c$id; + print hdr; + print elements; + } + +event gtpv1_update_pdp_ctx_response(c: connection, hdr: gtpv1_hdr, + elements: gtp_update_pdp_ctx_response_elements) + { + print "gtp update response", c$id; + print hdr; + print elements; + } + +event gtpv1_delete_pdp_ctx_request(c: connection, hdr: gtpv1_hdr, + elements: gtp_delete_pdp_ctx_request_elements) + { + print "gtp delete request", c$id; + print hdr; + print elements; + } + +event gtpv1_delete_pdp_ctx_response(c: connection, hdr: gtpv1_hdr, + elements: gtp_delete_pdp_ctx_response_elements) + { + print "gtp delete response", c$id; + print hdr; + print elements; + }