diff --git a/debian/changelog b/debian/changelog index c5fcc20..ff01f8f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -44,6 +44,12 @@ openli (1.1.6-1) unstable; urgency=medium * Added new parameter for IP intercepts: mobileident -- this is used to indicate whether the user identifier for a mobile data intercept is an MSISDN, IMEI or IMSI. +<<<<<<< HEAD + * VOIP intercepts that are written to pcap files will now include + their corresponding SIP packets alongside the RTP streams. + + -- Shane Alcock Wed, 22 May 2024 11:08:41 +1200 +======= * Add support for including SIP packets in pcapdisk output for VoIP intercepts. * Fix bug where mediators receiving message from a collector via @@ -57,6 +63,7 @@ openli (1.1.6-1) unstable; urgency=medium blocked. -- Shane Alcock Mon, 1 Jul 2024 09:57:07 +1200 +>>>>>>> master openli (1.1.5-1) unstable; urgency=medium diff --git a/src/Makefile.am b/src/Makefile.am index 9e9e2a3..4837936 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -71,7 +71,10 @@ openlicollector_SOURCES=collector/collector.c configparser.c configparser.h \ collector/etsiencoding/etsiencoding.c \ collector/etsiencoding/encryptcontainer.c \ collector/etsiencoding/ipmmiri.c \ + collector/etsiencoding/epsiri.c collector/epsiri.h \ collector/sms_worker.c collector/sms_worker.h \ + collector/gtp_worker.c collector/gtp_worker.h \ + collector/gtp.h \ collector/location.c collector/location.h \ collector/collector_util.c collector/collector_util.h \ $(PLUGIN_SRCS) diff --git a/src/collector/accessplugins/gtp.c b/src/collector/accessplugins/gtp.c index c0fe219..6ddfc66 100644 --- a/src/collector/accessplugins/gtp.c +++ b/src/collector/accessplugins/gtp.c @@ -33,13 +33,17 @@ #include "logger.h" #include "internetaccess.h" #include "util.h" +#include "gtp.h" +#include "epsiri.h" #define GTP_FLUSH_OLD_PKT_FREQ 180 enum { GTPV1_IE_CAUSE = 1, GTPV1_IE_IMSI = 2, + GTPV1_IE_TEID_DATA = 16, GTPV1_IE_TEID_CTRL = 17, + GTPV1_IE_RAT_TYPE = 82, GTPV1_IE_END_USER_ADDRESS = 128, GTPV1_IE_APNAME = 131, GTPV1_IE_MSISDN = 134, @@ -51,11 +55,17 @@ enum { GTPV2_IE_IMSI = 1, GTPV2_IE_CAUSE = 2, GTPV2_IE_APNAME = 71, + GTPV2_IE_AMBR = 72, + GTPV2_IE_BEARER_ID = 73, GTPV2_IE_MEI = 75, GTPV2_IE_MSISDN = 76, + GTPV2_IE_PCO = 78, GTPV2_IE_PDN_ALLOC = 79, + GTPV2_IE_RAT_TYPE = 82, GTPV2_IE_ULI = 86, GTPV2_IE_FTEID = 87, + GTPV2_IE_BEARER_CONTEXT = 93, + GTPV2_IE_PDN_TYPE = 99, }; /* TODO add more cause values here */ @@ -65,45 +75,17 @@ enum { enum { GTPV1_CAUSE_REQUEST_ACCEPTED = 128, + GTPV1_CAUSE_SYSTEM_FAILURE = 204, + GTPV1_CAUSE_AUTH_FAILED = 209, }; enum { + SM_CAUSE_USER_AUTH_FAILED = 29, + SM_CAUSE_UNSUPPORTED_OPTION = 32, + SM_CAUSE_TEMP_OUT_OF_ORDER = 34, SM_CAUSE_REGULAR_DEACTIVATION = 36, }; -/* XXX do we need to support other message types here, e.g. for IRI-Report? */ -enum { - GTPV1_CREATE_PDP_CONTEXT_REQUEST = 16, - GTPV1_CREATE_PDP_CONTEXT_RESPONSE = 17, - GTPV1_UPDATE_PDP_CONTEXT_REQUEST = 18, - GTPV1_UPDATE_PDP_CONTEXT_RESPONSE = 19, - GTPV1_DELETE_PDP_CONTEXT_REQUEST = 20, - GTPV1_DELETE_PDP_CONTEXT_RESPONSE = 21, - - GTPV2_CREATE_SESSION_REQUEST = 32, - GTPV2_CREATE_SESSION_RESPONSE = 33, - GTPV2_DELETE_SESSION_REQUEST = 36, - GTPV2_DELETE_SESSION_RESPONSE = 37, -}; - -typedef struct gtpv1_header { - uint8_t octet1; - uint8_t msgtype; - uint16_t msglen; - uint32_t teid; - uint16_t seqno; - uint8_t npdu; - uint8_t next_ext; -} PACKED gtpv1_header_t; - -typedef struct gtpv2_header_teid { - uint8_t octet1; - uint8_t msgtype; - uint16_t msglen; - uint32_t teid; - uint32_t seqno; -} PACKED gtpv2_header_teid_t; - typedef struct gtp_infelem gtp_infoelem_t; struct gtp_infelem { @@ -136,6 +118,7 @@ typedef struct gtp_saved_packet gtp_saved_pkt_t; typedef struct gtp_session { char *sessid; + char *altsessid; gtp_sess_saved_t saved; char idstr_msisdn[64]; @@ -144,18 +127,21 @@ typedef struct gtp_session { int idstr_imsi_len; char idstr_imei[64]; int idstr_imei_len; - uint32_t teid; + uint32_t control_teid[2]; + uint32_t data_teid[2]; internetaccess_ip_t *pdpaddrs; uint8_t pdpaddrcount; uint16_t pdptype; int64_t cin; + uint8_t gtpversion; uint8_t serverid[16]; uint8_t serveripfamily; - session_state_t current; + char *tunnel_endpoints; + session_state_t current; uint64_t last_reqid; uint8_t last_reqtype; @@ -163,6 +149,9 @@ typedef struct gtp_session { session_state_t savednewstate; gtp_saved_pkt_t *lastsavedpkt; + int refcount; + uint8_t defaultbearer; + } gtp_session_t; struct gtp_saved_packet { @@ -172,6 +161,10 @@ struct gtp_saved_packet { uint8_t applied; double tvsec; uint8_t response_cause; + uint8_t bearerid; + uint32_t teid_ctl; + uint32_t teid_data; + uint32_t teid; uint8_t *ipcontent; uint16_t iplen; @@ -189,12 +182,15 @@ typedef struct gtp_parsed { double tvsec; uint32_t teid; uint32_t teid_ctl; + uint32_t teid_data; uint32_t seqno; uint8_t response_cause; + uint8_t bearerid; uint8_t serveripfamily; uint8_t serverid[16]; + char tunnel_endpoints[256]; char imsi[16]; char msisdn[16]; char imei[16]; @@ -214,7 +210,7 @@ typedef struct gtp_global { Pvoid_t saved_packets; Pvoid_t session_map; - Pvoid_t alt_session_map; + Pvoid_t data_sessions; double lastrefresh; } gtp_global_t; @@ -228,10 +224,13 @@ static void reset_parsed_pkt(gtp_parsed_t *parsed) { parsed->tvsec = 0; parsed->teid = 0; parsed->teid_ctl = 0; + parsed->teid_data = 0; parsed->seqno = 0; parsed->response_cause = 0; + parsed->bearerid = 255; parsed->serveripfamily = 0; + memset(parsed->tunnel_endpoints, 0, 256); memset(parsed->serverid, 0, 16); memset(parsed->imsi, 0, 16); memset(parsed->imei, 0, 16); @@ -244,6 +243,14 @@ static void reset_parsed_pkt(gtp_parsed_t *parsed) { parsed->response = NULL; } +#define GEN_SESSID(sessid, serveripfamily, serverid, teid) \ + if (serveripfamily == 4) { \ + snprintf(sessid, 64, "%u-%u", *(uint32_t *)serverid, teid); \ + } else if (serveripfamily == 6) { \ + snprintf(sessid, 64, "%lu-%lu-%u", *(uint64_t *)serverid, \ + *(uint64_t *)(serverid + 8), teid); \ + } + static void gtp_init_plugin_data(access_plugin_t *p) { gtp_global_t *glob; @@ -264,6 +271,14 @@ static inline void destroy_gtp_session(gtp_session_t *sess) { free(sess->sessid); } + if (sess->tunnel_endpoints) { + free(sess->tunnel_endpoints); + } + + if (sess->altsessid) { + free(sess->altsessid); + } + if (sess->saved.imei) { free(sess->saved.imei); } @@ -306,8 +321,10 @@ static inline void gtp_free_ie_list(gtp_infoelem_t *ies) { static void gtp_destroy_plugin_data(access_plugin_t *p) { gtp_global_t *glob; unsigned char index[64]; + unsigned char altid[64]; PWord_t pval; Word_t res, indexnum; + int rc; glob = (gtp_global_t *)(p->plugindata); if (!glob) { @@ -318,11 +335,13 @@ static void gtp_destroy_plugin_data(access_plugin_t *p) { JSLF(pval, glob->session_map, index); while (pval) { gtp_session_t *sess = (gtp_session_t *)(*pval); + GEN_SESSID((char *)altid, sess->serveripfamily, sess->serverid, + sess->control_teid[1]); + JSLD(rc, glob->session_map, altid); destroy_gtp_session(sess); JSLN(pval, glob->session_map, index); } JSLFA(res, glob->session_map); - JSLFA(res, glob->alt_session_map); indexnum = 0; JLF(pval, glob->saved_packets, indexnum); @@ -338,10 +357,12 @@ static void gtp_destroy_plugin_data(access_plugin_t *p) { } JLFA(res, glob->saved_packets); + if (glob->parsedpkt) { free(glob->parsedpkt); } free(glob); + p->plugindata = NULL; } static void gtp_uncouple_parsed_data(access_plugin_t *p) { @@ -393,6 +414,12 @@ static inline bool interesting_info_element(uint8_t gtpv, uint8_t ietype) { case GTPV2_IE_MEI: case GTPV2_IE_APNAME: case GTPV2_IE_ULI: + case GTPV2_IE_BEARER_CONTEXT: + case GTPV2_IE_PCO: + case GTPV2_IE_RAT_TYPE: + case GTPV2_IE_AMBR: + case GTPV2_IE_BEARER_ID: + case GTPV2_IE_PDN_TYPE: return true; } } else if (gtpv == 1) { @@ -471,6 +498,108 @@ static inline uint8_t get_cause_from_ie(gtp_infoelem_t *gtpel) { return *((uint8_t *)(gtpel->iecontent)); } +static inline uint8_t get_bearer_id_from_ie(gtp_infoelem_t *gtpel) { + + return *((uint8_t *)(gtpel->iecontent)); +} + +static void parse_session_bearer_context(gtp_parsed_t *parsedpkt, + gtp_infoelem_t *el) { + + uint8_t *ptr = (uint8_t *)el->iecontent; + uint8_t *start = ptr; + uint8_t subtype; + uint16_t sublen; + uint32_t *teidkey; + + /* Need at least 5 bytes for a complete sub-IE (4 for header, plus at + * least one for the value) + */ + while (ptr - start < el->ielength) { + if (el->ielength - (ptr - start) <= 4) { + logger(LOG_INFO, "OpenLI: incomplete IE header while decoding GTPv2 Bearer Context Information Element"); + break; + } + + subtype = *ptr; + sublen = *(ptr + 1); + sublen = sublen << 8; + sublen += *(ptr + 2); + ptr += 4; + + if (el->ielength - (ptr - start) < sublen) { + logger(LOG_INFO, "OpenLI: truncated IE body while decoding GTPv2 Bearer Context Information Element"); + break; + } + + switch(subtype) { + case 0x49: // EPS Bearer ID + parsedpkt->bearerid = *ptr; + break; + case 0x57: // F-TEID + teidkey = (uint32_t *)(ptr + 1); + parsedpkt->teid_data = ntohl(*teidkey); + break; + } + ptr += sublen; + } +} + +static void walk_bearer_context_ie(etsili_generic_freelist_t *freelist, + gtp_infoelem_t *el, etsili_generic_t **params, uint8_t is_req) { + + uint8_t *ptr = (uint8_t *)el->iecontent; + uint8_t *start = ptr; + uint8_t subtype; + uint16_t sublen; + etsili_generic_t *np; + + /* Need at least 5 bytes for a complete sub-IE (4 for header, plus at + * least one for the value) + */ + while (ptr - start < el->ielength) { + np = NULL; + if (el->ielength - (ptr - start) <= 4) { + logger(LOG_INFO, "OpenLI: incomplete IE header while decoding GTPv2 Bearer Context Information Element"); + break; + } + + subtype = *ptr; + sublen = *(ptr + 1); + sublen = sublen << 8; + sublen += *(ptr + 2); + ptr += 4; + + if (el->ielength - (ptr - start) < sublen) { + logger(LOG_INFO, "OpenLI: truncated IE body while decoding GTPv2 Bearer Context Information Element"); + break; + } + + switch(subtype) { + case 0x49: // EPS Bearer ID + if (!is_req) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_BEARER_ID, sublen, ptr); + } + break; + case 0x57: // F-TEID + break; + case 0x50: // Bearer QoS + if (is_req) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_BEARER_QOS, sublen, ptr); + } + break; + } + + if (np) { + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + ptr += sublen; + } +} + static inline void get_gtpnum_from_ie(gtp_infoelem_t *gtpel, char *field, int skipfront) { @@ -575,9 +704,6 @@ static int walk_gtpv1_ies(gtp_parsed_t *parsedpkt, uint8_t *ptr, uint32_t rem, if (gtpel) { if (parsedpkt->msgtype == GTPV1_CREATE_PDP_CONTEXT_REQUEST) { - if (ietype == GTPV1_IE_TEID_CTRL) { - parsedpkt->teid = get_teid_from_teidctl(gtpel); - } if (ietype == GTPV1_IE_IMSI) { get_gtpnum_from_ie(gtpel, parsedpkt->imsi, 0); } @@ -587,6 +713,9 @@ static int walk_gtpv1_ies(gtp_parsed_t *parsedpkt, uint8_t *ptr, uint32_t rem, if (ietype == GTPV1_IE_MSISDN) { get_gtpnum_from_ie(gtpel, parsedpkt->msisdn, 1); } + if (ietype == GTPV1_IE_TEID_DATA) { + parsedpkt->teid_data = get_teid_from_teidctl(gtpel); + } } if (ietype == GTPV1_IE_TEID_CTRL) { @@ -630,9 +759,6 @@ static void walk_gtpv2_ies(gtp_parsed_t *parsedpkt, uint8_t *ptr, uint32_t rem, parsedpkt->ies = gtpel; if (parsedpkt->msgtype == GTPV2_CREATE_SESSION_REQUEST) { - if (ietype == GTPV2_IE_FTEID) { - parsedpkt->teid = get_teid_from_fteid(gtpel); - } if (ietype == GTPV2_IE_IMSI) { get_gtpnum_from_ie(gtpel, parsedpkt->imsi, 0); } @@ -642,12 +768,18 @@ static void walk_gtpv2_ies(gtp_parsed_t *parsedpkt, uint8_t *ptr, uint32_t rem, if (ietype == GTPV2_IE_MSISDN) { get_gtpnum_from_ie(gtpel, parsedpkt->msisdn, 0); } - } else if (parsedpkt->msgtype == GTPV2_DELETE_SESSION_REQUEST) { - if (ietype == GTPV2_IE_FTEID) { - parsedpkt->teid = get_teid_from_fteid(gtpel); - } } + if (ietype == GTPV2_IE_BEARER_CONTEXT) { + parse_session_bearer_context(parsedpkt, gtpel); + } + if (ietype == GTPV2_IE_BEARER_ID) { + parsedpkt->bearerid = get_bearer_id_from_ie(gtpel); + } + + if (ietype == GTPV2_IE_FTEID) { + parsedpkt->teid_ctl = get_teid_from_fteid(gtpel); + } if (ietype == GTPV2_IE_CAUSE) { parsedpkt->response_cause = get_cause_from_ie(gtpel); } @@ -721,6 +853,9 @@ static int gtp_parse_v2_teid(gtp_global_t *glob, libtrace_packet_t *pkt, walk_gtpv2_ies(glob->parsedpkt, ptr, rem, len); + if (glob->parsedpkt->teid == 0) { + glob->parsedpkt->teid = glob->parsedpkt->teid_ctl; + } return 0; } @@ -762,6 +897,10 @@ static int gtp_parse_v1_teid(gtp_global_t *glob, libtrace_packet_t *pkt, return -1; } + if (glob->parsedpkt->teid == 0) { + glob->parsedpkt->teid = glob->parsedpkt->teid_ctl; + } + return 0; } @@ -814,9 +953,26 @@ static void *gtp_parse_packet(access_plugin_t *p, libtrace_packet_t *pkt) { glob->parsedpkt->serveripfamily = 4; + /* It appears that update bearer requests are sent by the server. + * Every other request so far is sent by the UE / client */ + + if (ip->ip_src.s_addr < ip->ip_dst.s_addr) { + snprintf(glob->parsedpkt->tunnel_endpoints, 256, "%u-%u", + ip->ip_src.s_addr, ip->ip_dst.s_addr); + } else { + snprintf(glob->parsedpkt->tunnel_endpoints, 256, "%u-%u", + ip->ip_dst.s_addr, ip->ip_src.s_addr); + } + switch(glob->parsedpkt->msgtype) { case GTPV2_CREATE_SESSION_REQUEST: case GTPV2_DELETE_SESSION_REQUEST: + case GTPV2_MODIFY_BEARER_REQUEST: + case GTPV2_CREATE_BEARER_REQUEST: + case GTPV2_DELETE_BEARER_REQUEST: + case GTPV2_UPDATE_BEARER_RESPONSE: + case GTPV2_MODIFY_BEARER_COMMAND: + case GTPV2_DELETE_BEARER_COMMAND: case GTPV1_CREATE_PDP_CONTEXT_REQUEST: case GTPV1_UPDATE_PDP_CONTEXT_REQUEST: case GTPV1_DELETE_PDP_CONTEXT_REQUEST: @@ -824,6 +980,12 @@ static void *gtp_parse_packet(access_plugin_t *p, libtrace_packet_t *pkt) { break; case GTPV2_CREATE_SESSION_RESPONSE: case GTPV2_DELETE_SESSION_RESPONSE: + case GTPV2_MODIFY_BEARER_RESPONSE: + case GTPV2_CREATE_BEARER_RESPONSE: + case GTPV2_UPDATE_BEARER_REQUEST: + case GTPV2_DELETE_BEARER_RESPONSE: + case GTPV2_MODIFY_BEARER_FAILURE_INDICATION: + case GTPV2_DELETE_BEARER_FAILURE_INDICATION: case GTPV1_CREATE_PDP_CONTEXT_RESPONSE: case GTPV1_UPDATE_PDP_CONTEXT_RESPONSE: case GTPV1_DELETE_PDP_CONTEXT_RESPONSE: @@ -838,9 +1000,29 @@ static void *gtp_parse_packet(access_plugin_t *p, libtrace_packet_t *pkt) { glob->parsedpkt->serveripfamily = 6; + if (memcmp(&(ip6->ip_src.s6_addr), &(ip6->ip_dst.s6_addr), 16) < 0) { + snprintf(glob->parsedpkt->tunnel_endpoints, 256, "%lu-%lu-%lu-%lu", + *(uint64_t *)(&(ip6->ip_src.s6_addr)), + *(uint64_t *)(&(ip6->ip_src.s6_addr[8])), + *(uint64_t *)(&(ip6->ip_dst.s6_addr)), + *(uint64_t *)(&(ip6->ip_dst.s6_addr[8]))); + } else { + snprintf(glob->parsedpkt->tunnel_endpoints, 256, "%lu-%lu-%lu-%lu", + *(uint64_t *)(&(ip6->ip_dst.s6_addr)), + *(uint64_t *)(&(ip6->ip_dst.s6_addr[8])), + *(uint64_t *)(&(ip6->ip_src.s6_addr)), + *(uint64_t *)(&(ip6->ip_src.s6_addr[8]))); + } + switch(glob->parsedpkt->msgtype) { case GTPV2_CREATE_SESSION_REQUEST: case GTPV2_DELETE_SESSION_REQUEST: + case GTPV2_MODIFY_BEARER_REQUEST: + case GTPV2_CREATE_BEARER_REQUEST: + case GTPV2_UPDATE_BEARER_RESPONSE: + case GTPV2_DELETE_BEARER_REQUEST: + case GTPV2_MODIFY_BEARER_COMMAND: + case GTPV2_DELETE_BEARER_COMMAND: case GTPV1_CREATE_PDP_CONTEXT_REQUEST: case GTPV1_UPDATE_PDP_CONTEXT_REQUEST: case GTPV1_DELETE_PDP_CONTEXT_REQUEST: @@ -849,6 +1031,12 @@ static void *gtp_parse_packet(access_plugin_t *p, libtrace_packet_t *pkt) { break; case GTPV2_CREATE_SESSION_RESPONSE: case GTPV2_DELETE_SESSION_RESPONSE: + case GTPV2_MODIFY_BEARER_RESPONSE: + case GTPV2_CREATE_BEARER_RESPONSE: + case GTPV2_UPDATE_BEARER_REQUEST: + case GTPV2_DELETE_BEARER_RESPONSE: + case GTPV2_MODIFY_BEARER_FAILURE_INDICATION: + case GTPV2_DELETE_BEARER_FAILURE_INDICATION: case GTPV1_CREATE_PDP_CONTEXT_RESPONSE: case GTPV1_UPDATE_PDP_CONTEXT_RESPONSE: case GTPV1_DELETE_PDP_CONTEXT_RESPONSE: @@ -933,25 +1121,15 @@ static void save_identifier_strings(gtp_parsed_t *gparsed, gtp_session_t *sess) } } -#define GEN_SESSID(sessid, gparsed, teid) \ - if (gparsed->serveripfamily == 4) { \ - snprintf(sessid, 64, "%u-%u", *(uint32_t *)gparsed->serverid, teid); \ - } else if (gparsed->serveripfamily == 6) { \ - snprintf(sessid, 64, "%lu-%lu-%u", *(uint64_t *)gparsed->serverid, \ - *(uint64_t *)(gparsed->serverid + 8), teid); \ - } - static user_identity_t *gtp_get_userid(access_plugin_t *p, void *parsed, int *numberids) { gtp_global_t *glob = (gtp_global_t *)(p->plugindata); gtp_parsed_t *gparsed = (gtp_parsed_t *)parsed; unsigned char sessid[64]; - unsigned char alt_sessid[64]; gtp_session_t *sess; PWord_t pval; user_identity_t *uids; - Pvoid_t search; if (glob == NULL || gparsed == NULL) { return NULL; @@ -967,16 +1145,9 @@ static user_identity_t *gtp_get_userid(access_plugin_t *p, void *parsed, } /* Need to look up the session */ - GEN_SESSID((char *)sessid, gparsed, gparsed->teid); - - if (gparsed->msgtype == GTPV1_DELETE_PDP_CONTEXT_REQUEST || - gparsed->msgtype == GTPV1_UPDATE_PDP_CONTEXT_REQUEST) { - search = glob->alt_session_map; - } else { - search = glob->session_map; - } - - JSLG(pval, search, sessid); + GEN_SESSID((char *)sessid, gparsed->serveripfamily, gparsed->serverid, + gparsed->teid); + JSLG(pval, glob->session_map, sessid); if (pval) { gparsed->matched_session = (gtp_session_t *)(*pval); @@ -986,23 +1157,10 @@ static user_identity_t *gtp_get_userid(access_plugin_t *p, void *parsed, save_identifier_strings(gparsed, gparsed->matched_session); } - if (search == glob->alt_session_map) { - gparsed->teid = gparsed->matched_session->teid; - } - - /* v1 delete requests use the teid_cp from the create response - * as their TEID, so we need to have a reference to this session - * for that TEID as well. Otherwise we'll miss the delete requests. - */ - if (gparsed->msgtype == GTPV1_CREATE_PDP_CONTEXT_RESPONSE) { - GEN_SESSID((char *)alt_sessid, gparsed, gparsed->teid_ctl); - JSLG(pval, glob->alt_session_map, alt_sessid); - - if (!pval) { - JSLI(pval, glob->alt_session_map, alt_sessid); - *pval = (Word_t)gparsed->matched_session; + if (gparsed->msgtype == GTPV2_CREATE_SESSION_RESPONSE) { + if (gparsed->bearerid != 255) { + gparsed->matched_session->defaultbearer = gparsed->bearerid; } - } uids = copy_identifiers(gparsed, numberids); @@ -1020,12 +1178,16 @@ static user_identity_t *gtp_get_userid(access_plugin_t *p, void *parsed, ((uint64_t)gparsed->seqno); saved->ies = gparsed->ies; saved->version = gparsed->version; + saved->teid_ctl = gparsed->teid_ctl; + saved->teid_data = gparsed->teid_data; + saved->teid = gparsed->teid; saved->matched_session = NULL; saved->applied = 0; saved->tvsec = gparsed->tvsec; saved->ipcontent = NULL; saved->iplen = 0; saved->response_cause = gparsed->response_cause; + saved->bearerid = gparsed->bearerid; gparsed->ies = NULL; openli_copy_ipcontent(gparsed->origpkt, &(saved->ipcontent), @@ -1048,16 +1210,19 @@ static user_identity_t *gtp_get_userid(access_plugin_t *p, void *parsed, sess = calloc(1, sizeof(gtp_session_t)); sess->sessid = strdup((char *)sessid); sess->current = SESSION_STATE_NEW; - sess->teid = gparsed->teid; + sess->control_teid[0] = gparsed->teid_ctl; + sess->data_teid[0] = gparsed->teid_data; sess->pdpaddrs = NULL; sess->pdpaddrcount = 0; - + sess->refcount = 0; + sess->gtpversion = gparsed->version; + sess->defaultbearer = 255; memcpy(sess->serverid, gparsed->serverid, 16); + sess->tunnel_endpoints = strdup(gparsed->tunnel_endpoints); sess->serveripfamily = gparsed->serveripfamily; JSLI(pval, glob->session_map, (unsigned char *)sess->sessid); *pval = (Word_t)sess; - gparsed->matched_session = sess; save_identifier_strings(gparsed, sess); @@ -1244,7 +1409,46 @@ static void copy_session_params_v2(gtp_parsed_t *gparsed, } } -static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, +static void create_alt_session_entry(gtp_global_t *glob, + gtp_parsed_t *gparsed, uint32_t teid_ctl) { + + unsigned char alt_sessid[64]; + PWord_t pval; + + /* GTP requests during the session (including DELETEs) + * use the teid_cp from the create response + * as their TEID, so we need to have a reference to this session + * for that TEID as well. Otherwise we'll miss the delete requests. + */ + GEN_SESSID((char *)alt_sessid, gparsed->serveripfamily, + gparsed->serverid, teid_ctl); + + if (gparsed->matched_session->altsessid) { + free(gparsed->matched_session->altsessid); + } + gparsed->matched_session->altsessid = strdup((char *)alt_sessid); + gparsed->matched_session->control_teid[1] = teid_ctl; + + JSLG(pval, glob->session_map, alt_sessid); + if (!pval) { + JSLI(pval, glob->session_map, + (uint8_t *)gparsed->matched_session->altsessid); + *pval = (Word_t)gparsed->matched_session; + } +} + +static inline void add_new_session_teids(access_session_t *sess, + gtp_session_t *gsess) { + + sess->teids[0] = gsess->data_teid[0]; + sess->teids[1] = gsess->data_teid[1]; + sess->gtp_tunnel_endpoints = strdup(gsess->tunnel_endpoints); + sess->identifier_type |= OPENLI_ACCESS_SESSION_TEID; + +} + +static void apply_gtp_fsm_logic(gtp_global_t *glob, + gtp_parsed_t *gparsed, access_action_t *action, access_session_t *sess, gtp_saved_pkt_t *gpkt, session_state_t current) { @@ -1254,10 +1458,24 @@ static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, copy_session_params_v2(gparsed, gpkt); } + + /* TODO add appropriate action updates for: + * GTPV2_MODIFY_BEARER_COMMAND + * GTPV2_MODIFY_BEARER_FAILURE_INDICATION + * GTPV2_DELETE_BEARER_COMMAND + * GTPV2_DELETE_BEARER_FAILURE_INDICATION + * GTPV2_CREATE_BEARER_RESPONSE + * GTPV2_DELETE_BEARER_RESPONSE + */ + if (current == SESSION_STATE_NEW && (gpkt->type == GTPV2_CREATE_SESSION_REQUEST || gpkt->type == GTPV1_CREATE_PDP_CONTEXT_REQUEST)) { + if (gpkt->bearerid != 255 && gpkt->version == 2) { + gparsed->matched_session->defaultbearer = gpkt->bearerid; + } + current = SESSION_STATE_AUTHING; *action = ACCESS_ACTION_NONE; @@ -1271,6 +1489,15 @@ static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, extract_gtp_assigned_ip_address(gpkt, sess, gparsed->matched_session); + /* TODO: set up GTP-U data sessions */ + + if (gpkt->teid_ctl != 0) { + create_alt_session_entry(glob, gparsed, gpkt->teid_ctl); + } + if (gpkt->teid_data != 0) { + gparsed->matched_session->data_teid[1] = gpkt->teid_data; + } + add_new_session_teids(sess, gparsed->matched_session); } else if (gpkt->response_cause >= 64 && gpkt->response_cause <= 239) { current = SESSION_STATE_OVER; *action = ACCESS_ACTION_REJECT; @@ -1284,6 +1511,16 @@ static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, extract_gtp_assigned_ip_address(gpkt, sess, gparsed->matched_session); + + /* TODO: set up GTP-U data sessions */ + if (gpkt->teid_ctl != 0) { + create_alt_session_entry(glob, gparsed, gpkt->teid_ctl); + } + if (gpkt->teid_data != 0) { + gparsed->matched_session->data_teid[1] = gpkt->teid_data; + } + add_new_session_teids(sess, gparsed->matched_session); + } else if (gpkt->response_cause >= 192) { current = SESSION_STATE_OVER; *action = ACCESS_ACTION_REJECT; @@ -1301,6 +1538,12 @@ static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, *action = ACCESS_ACTION_END; } else if (current == SESSION_STATE_ACTIVE && (gpkt->type == GTPV1_UPDATE_PDP_CONTEXT_RESPONSE)) { + *action = ACCESS_ACTION_MODIFIED; + } else if (current == SESSION_STATE_ACTIVE && + (gpkt->type == GTPV2_MODIFY_BEARER_RESPONSE)) { + *action = ACCESS_ACTION_MODIFIED; + } else if (current == SESSION_STATE_ACTIVE && + (gpkt->type == GTPV2_UPDATE_BEARER_RESPONSE)) { *action = ACCESS_ACTION_INTERIM_UPDATE; } @@ -1309,7 +1552,8 @@ static void apply_gtp_fsm_logic(gtp_parsed_t *gparsed, access_action_t *action, } static inline access_session_t *find_matched_session(access_plugin_t *p, - access_session_t **sesslist, gtp_session_t *match, uint32_t teid) { + access_session_t **sesslist, gtp_session_t *match, + uint8_t incr_refcount) { access_session_t *thissess = NULL; @@ -1322,8 +1566,11 @@ static inline access_session_t *find_matched_session(access_plugin_t *p, if (!thissess) { thissess = create_access_session(p, match->sessid, strlen(match->sessid)); - thissess->cin = assign_gtp_cin(teid); + thissess->cin = assign_gtp_cin(match->control_teid[0]); match->cin = thissess->cin; + if (incr_refcount) { + match->refcount ++; + } HASH_ADD_KEYPTR(hh, *sesslist, thissess->sessionid, strlen(thissess->sessionid), thissess); @@ -1342,14 +1589,25 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, gtp_parsed_t *gparsed = (gtp_parsed_t *)parsed; PWord_t pval; Word_t rcint; - uint64_t reqid = (((uint64_t)gparsed->teid) << 32) | - ((uint64_t)gparsed->seqno); + uint64_t reqid; + uint8_t incr_refcount = 0; + if (gparsed->matched_session == NULL) { *action = ACCESS_ACTION_NONE; return NULL; } + reqid = (((uint64_t)gparsed->matched_session->control_teid[0]) << 32) | + ((uint64_t)gparsed->seqno); + + if (gparsed->msgtype == GTPV2_CREATE_SESSION_REQUEST || + gparsed->msgtype == GTPV1_CREATE_PDP_CONTEXT_REQUEST) { + incr_refcount = 1; + } else { + incr_refcount = 0; + } + if (reqid == gparsed->matched_session->last_reqid && gparsed->msgtype == gparsed->matched_session->last_reqtype) { @@ -1358,13 +1616,29 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, * the packet. */ thissess = find_matched_session(p, sesslist, gparsed->matched_session, - gparsed->teid); + incr_refcount); *oldstate = gparsed->matched_session->savedoldstate; *action = gparsed->action; *newstate = gparsed->matched_session->savednewstate; return thissess; } + if (gparsed->msgtype == GTPV2_MODIFY_BEARER_COMMAND || + gparsed->msgtype == GTPV2_DELETE_BEARER_COMMAND) { + + /* TODO */ + gparsed->matched_session = NULL; + *action = ACCESS_ACTION_NONE; + return NULL; + } else if (gparsed->msgtype == GTPV2_MODIFY_BEARER_FAILURE_INDICATION || + gparsed->msgtype == GTPV2_DELETE_BEARER_FAILURE_INDICATION) { + + /* TODO */ + gparsed->matched_session = NULL; + *action = ACCESS_ACTION_NONE; + return NULL; + } + saved = calloc(1, sizeof(gtp_saved_pkt_t)); saved->type = gparsed->msgtype; @@ -1377,6 +1651,10 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, saved->ipcontent = NULL; saved->iplen = 0; saved->response_cause = gparsed->response_cause; + saved->bearerid = gparsed->bearerid; + saved->teid = gparsed->teid; + saved->teid_ctl = gparsed->teid_ctl; + saved->teid_data = gparsed->teid_data; gparsed->ies = NULL; gparsed->matched_session->last_reqid = reqid; @@ -1393,16 +1671,20 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, if (gparsed->msgtype == GTPV2_CREATE_SESSION_REQUEST || gparsed->msgtype == GTPV2_DELETE_SESSION_REQUEST || + gparsed->msgtype == GTPV2_MODIFY_BEARER_REQUEST || + gparsed->msgtype == GTPV2_UPDATE_BEARER_REQUEST || + gparsed->msgtype == GTPV2_DELETE_BEARER_REQUEST || + gparsed->msgtype == GTPV2_CREATE_BEARER_REQUEST || gparsed->msgtype == GTPV1_CREATE_PDP_CONTEXT_REQUEST || gparsed->msgtype == GTPV1_DELETE_PDP_CONTEXT_REQUEST || gparsed->msgtype == GTPV1_UPDATE_PDP_CONTEXT_REQUEST) { thissess = find_matched_session(p, sesslist, - gparsed->matched_session, gparsed->teid); + gparsed->matched_session, incr_refcount); if (thissess) { *oldstate = gparsed->matched_session->current; gparsed->matched_session->savedoldstate = *oldstate; - apply_gtp_fsm_logic(gparsed, &(gparsed->action), thissess, + apply_gtp_fsm_logic(glob, gparsed, &(gparsed->action), thissess, saved, *oldstate); *newstate = gparsed->matched_session->current; gparsed->matched_session->savednewstate = *newstate; @@ -1417,19 +1699,28 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, } else { check = (gtp_saved_pkt_t *)*pval; - + incr_refcount = 0; JLD(rcint, glob->saved_packets, check->reqid); if (saved->type == GTPV2_CREATE_SESSION_REQUEST && check->type == GTPV2_CREATE_SESSION_RESPONSE) { - gparsed->request = saved; gparsed->response = check; + incr_refcount = 1; } else if (check->type == GTPV2_CREATE_SESSION_REQUEST && saved->type == GTPV2_CREATE_SESSION_RESPONSE) { gparsed->request = check; gparsed->response = saved; + incr_refcount = 1; + } else if (saved->type == GTPV2_MODIFY_BEARER_REQUEST && + check->type == GTPV2_MODIFY_BEARER_RESPONSE) { + gparsed->request = saved; + gparsed->response = check; + } else if (check->type == GTPV2_MODIFY_BEARER_REQUEST && + saved->type == GTPV2_MODIFY_BEARER_RESPONSE) { + gparsed->request = check; + gparsed->response = saved; } else if (saved->type == GTPV2_DELETE_SESSION_REQUEST && check->type == GTPV2_DELETE_SESSION_RESPONSE) { gparsed->request = saved; @@ -1443,11 +1734,13 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, gparsed->request = saved; gparsed->response = check; + incr_refcount = 1; } else if (check->type == GTPV1_CREATE_PDP_CONTEXT_REQUEST && saved->type == GTPV1_CREATE_PDP_CONTEXT_RESPONSE) { gparsed->request = check; gparsed->response = saved; + incr_refcount = 1; } else if (saved->type == GTPV1_DELETE_PDP_CONTEXT_REQUEST && check->type == GTPV1_DELETE_PDP_CONTEXT_RESPONSE) { gparsed->request = saved; @@ -1483,13 +1776,13 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, if (gparsed->request->matched_session) { thissess = find_matched_session(p, sesslist, - gparsed->request->matched_session, gparsed->teid); + gparsed->request->matched_session, incr_refcount); *oldstate = gparsed->request->matched_session->current; gparsed->matched_session = gparsed->request->matched_session; gparsed->matched_session->savedoldstate = *oldstate; } else if (gparsed->response->matched_session) { thissess = find_matched_session(p, sesslist, - gparsed->response->matched_session, gparsed->teid); + gparsed->response->matched_session, 0); *oldstate = gparsed->response->matched_session->current; gparsed->matched_session = gparsed->response->matched_session; gparsed->matched_session->savedoldstate = *oldstate; @@ -1497,12 +1790,12 @@ static access_session_t *gtp_update_session_state(access_plugin_t *p, if (thissess) { if (gparsed->request->applied == 0) { - apply_gtp_fsm_logic(gparsed, &(gparsed->action), thissess, + apply_gtp_fsm_logic(glob, gparsed, &(gparsed->action), thissess, gparsed->request, gparsed->matched_session->current); gparsed->request->applied = 1; } if (gparsed->response->applied == 0) { - apply_gtp_fsm_logic(gparsed, &(gparsed->action), thissess, + apply_gtp_fsm_logic(glob, gparsed, &(gparsed->action), thissess, gparsed->response, gparsed->matched_session->current); gparsed->response->applied = 1; } @@ -1589,7 +1882,277 @@ static void parse_uli_v2(uint8_t *locinfo, } -static int gtp_create_pdp_generic_iri(gtp_parsed_t *gparsed, +static void parse_gtpv2_cause(gtp_parsed_t *gparsed, gtp_infoelem_t *el, + etsili_generic_freelist_t *freelist, etsili_generic_t **params) { + uint8_t *ptr = el->iecontent; + etsili_generic_t *np; + + if (gparsed->request->type == GTPV2_CREATE_SESSION_REQUEST) { + if ((*ptr) > 64) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_FAILED_BEARER_ACTIVATION_REASON, + el->ielength, ptr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + } + + else if (gparsed->request->type == GTPV2_MODIFY_BEARER_REQUEST) { + if ((*ptr) > 64) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_FAILED_BEARER_MODIFICATION_REASON, + el->ielength, ptr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + } + + else if (gparsed->request->type == GTPV2_DELETE_SESSION_REQUEST) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_BEARER_DEACTIVATION_CAUSE, + el->ielength, ptr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + +} + +static int gtp_create_eps_generic_iri(gtp_parsed_t *gparsed, + gtp_session_t *gsess, + etsili_generic_t **params, etsili_generic_freelist_t *freelist, + uint32_t evtype) { + + etsili_generic_t *np; + etsili_ipaddress_t ipaddr, netelipaddr; + uint32_t initiator = 1; + struct timeval tv; + gtp_infoelem_t *el; + + /* + * - EVENT_TIME = timestamp + * - INITIATOR + * - IMEI + * - IMSI + * - MSISDN + * - EVENT_TYPE + * - APN + * - PDN Address type and addresses + * - Operator Identifier (added later by encoder thread) + * - Correlation Number = CIN + * + * RAW INFORMATION ELEMENTS REQUIRED + * + * PDN Address Allocation + * APN + * PDN Type + * Bearer QOS + * Bearer activation type + * APN-AMBR + * Protocol Configuration Options + * Bearer ID + * Procedure Transaction Identifier ? + * RAT Type + * + */ + + if (gsess->serveripfamily == 4) { + etsili_create_ipaddress_v4((uint32_t *)(gsess->serverid), + ETSILI_IPV4_SUBNET_UNKNOWN, ETSILI_IPADDRESS_ASSIGNED_UNKNOWN, + &ipaddr); + etsili_create_ipaddress_v4((uint32_t *)(gsess->serverid), + ETSILI_IPV4_SUBNET_UNKNOWN, ETSILI_IPADDRESS_ASSIGNED_UNKNOWN, + &netelipaddr); + } else { + etsili_create_ipaddress_v6((uint8_t *)(gsess->serverid), + ETSILI_IPV6_SUBNET_UNKNOWN, ETSILI_IPADDRESS_ASSIGNED_UNKNOWN, + &ipaddr); + etsili_create_ipaddress_v6((uint8_t *)(gsess->serverid), + ETSILI_IPV6_SUBNET_UNKNOWN, ETSILI_IPADDRESS_ASSIGNED_UNKNOWN, + &netelipaddr); + } + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_GGSN_IPADDRESS, + sizeof(etsili_ipaddress_t), (uint8_t *)&ipaddr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_NETWORK_ELEMENT_IPADDRESS, + sizeof(etsili_ipaddress_t), (uint8_t *)&netelipaddr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_IMSI, + gsess->saved.imsi_len, (uint8_t *)gsess->saved.imsi); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_IMEI, + gsess->saved.imei_len, (uint8_t *)gsess->saved.imei); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_MSISDN, + gsess->saved.msisdn_len, (uint8_t *)gsess->saved.msisdn); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_APNAME, + gsess->saved.apname_len, (uint8_t *)gsess->saved.apname); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + + /* TODO encode all PDP addresses according to the standards */ + if (gsess->pdpaddrcount > 0) { + if (gsess->pdpaddrs[0].ipfamily == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) + &(gsess->pdpaddrs[0].assignedip); + + etsili_create_ipaddress_v4((uint32_t *)&(sin->sin_addr.s_addr), + ETSILI_IPV4_SUBNET_UNKNOWN, + ETSILI_IPADDRESS_ASSIGNED_DYNAMIC, + &ipaddr); + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) + &(gsess->pdpaddrs[0].assignedip); + + etsili_create_ipaddress_v6((uint8_t *)(sin6->sin6_addr.s6_addr), + ETSILI_IPV6_SUBNET_UNKNOWN, + ETSILI_IPADDRESS_ASSIGNED_DYNAMIC, + &ipaddr); + } + } + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_PDP_ADDRESS, + sizeof(etsili_ipaddress_t), (uint8_t *)&ipaddr); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_PDPTYPE, + sizeof(uint16_t), (uint8_t *)&(gsess->pdptype)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_EVENT_TYPE, + sizeof(uint32_t), (uint8_t *)&(evtype)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_INITIATOR, + sizeof(uint32_t), (uint8_t *)&(initiator)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + if (gparsed) { + TIMESTAMP_TO_TV((&tv), gparsed->response->tvsec); + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_EVENT_TIME, + sizeof(struct timeval), (uint8_t *)(&tv)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + TIMESTAMP_TO_TV((&tv), gparsed->request->tvsec); + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_LOCATION_TIME, + sizeof(struct timeval), (uint8_t *)(&tv)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } else { + gettimeofday(&tv, NULL); + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_EVENT_TIME, + sizeof(struct timeval), (uint8_t *)(&tv)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_LOCATION_TIME, + sizeof(struct timeval), (uint8_t *)(&tv)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + + if (gsess->saved.location) { + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_RAW_ULI, + gsess->saved.location_len, gsess->saved.location); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + } + + np = create_etsili_generic(freelist, EPSIRI_CONTENTS_GPRS_CORRELATION, + sizeof(int64_t), (uint8_t *)(&(gsess->cin))); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), + np); + + if (gparsed->request) { + el = gparsed->request->ies; + while (el) { + switch(el->ietype) { + case GTPV2_IE_PCO: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_PCO_FROM_UE, el->ielength, + (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + case GTPV2_IE_BEARER_CONTEXT: + walk_bearer_context_ie(freelist, el, params, 1); + break; + case GTPV2_IE_RAT_TYPE: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_RAT_TYPE, el->ielength, + (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + case GTPV2_IE_AMBR: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_APN_AMBR, el->ielength, + (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + case GTPV2_IE_ULI: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_ULI, el->ielength, + (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + case GTPV2_IE_PDN_TYPE: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_PDN_TYPE, el->ielength, + (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + + + + } + el = el->next; + } + + } + + if (gparsed->response) { + el = gparsed->response->ies; + while (el) { + switch(el->ietype) { + case GTPV2_IE_PDN_ALLOC: + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_PDN_ADDRESS_ALLOCATION, + el->ielength, (uint8_t *)(el->iecontent)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), + sizeof(np->itemnum), np); + break; + case GTPV2_IE_CAUSE: + parse_gtpv2_cause(gparsed, el, freelist, params); + break; + case GTPV2_IE_BEARER_CONTEXT: + walk_bearer_context_ie(freelist, el, params, 0); + break; + + } + el = el->next; + } + } + + + return 0; +} + +static int gtp_create_umts_generic_iri(gtp_parsed_t *gparsed, gtp_session_t *gsess, etsili_generic_t **params, etsili_generic_freelist_t *freelist, uint32_t evtype) { @@ -1712,7 +2275,8 @@ static int gtp_create_pdp_generic_iri(gtp_parsed_t *gparsed, } -static inline uint8_t gtpv2_cause_to_sm(uint8_t *gtpcause) { +static inline uint8_t gtpv2_cause_to_sm(uint8_t *gtpcause, + uint32_t evtype UNUSED) { switch(*gtpcause) { case GTPV2_CAUSE_REQUEST_ACCEPTED: @@ -1722,13 +2286,81 @@ static inline uint8_t gtpv2_cause_to_sm(uint8_t *gtpcause) { return 0; } -static inline uint8_t gtpv1_cause_to_sm(uint8_t *gtpcause) { +static inline uint8_t gtpv1_cause_to_sm(uint8_t *gtpcause, uint32_t evtype) { switch(*gtpcause) { case GTPV1_CAUSE_REQUEST_ACCEPTED: - return SM_CAUSE_REGULAR_DEACTIVATION; + if (evtype == UMTSIRI_EVENT_TYPE_PDPCONTEXT_DEACTIVATION) { + return SM_CAUSE_REGULAR_DEACTIVATION; + } + break; + case GTPV1_CAUSE_AUTH_FAILED: + return SM_CAUSE_USER_AUTH_FAILED; + case GTPV1_CAUSE_SYSTEM_FAILURE: + return SM_CAUSE_TEMP_OUT_OF_ORDER; + } + + return 0; +} + +static void insert_gtp_cause_as_gprs_error(gtp_infoelem_t *el, + etsili_generic_t **params, etsili_generic_freelist_t *freelist, + uint32_t evtype) { + + uint8_t smcauseval = 0; + uint8_t *attrptr = (uint8_t *)el->iecontent; + etsili_generic_t *np; + + if (el->ietype == GTPV2_IE_CAUSE) { + smcauseval = gtpv2_cause_to_sm(attrptr, evtype); + } else if (el->ietype == GTPV1_IE_CAUSE) { + smcauseval = gtpv1_cause_to_sm(attrptr, evtype); + } + + if (smcauseval != 0) { + np = create_etsili_generic(freelist, + UMTSIRI_CONTENTS_GPRS_ERROR_CODE, sizeof(uint8_t), + &(smcauseval)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } +} + +static int gtp_create_bearer_deactivation_iri(gtp_parsed_t *gparsed, + etsili_generic_t **params, + etsili_generic_freelist_t *freelist) { + + uint32_t evtype = EPSIRI_EVENT_TYPE_BEARER_DEACTIVATION; + etsili_generic_t *np = NULL; + uint32_t bearertype = 0; + uint32_t linkedbearer = 0; + + gtp_create_eps_generic_iri(gparsed, gparsed->matched_session, + params, freelist, evtype); + + /* Bearer ID is only in the DELETE request, not the response so can't + * rely on gparsed itself */ + if (gparsed->request->bearerid == gparsed->matched_session->defaultbearer) { + if (gparsed->request->bearerid != 255) { + bearertype = 1; + } + } else if (gparsed->request->bearerid != 255) { + bearertype = 2; } + if (bearertype != 0) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_BEARER_DEACTIVATION_TYPE, + sizeof(bearertype), (uint8_t *)(&bearertype)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } + + if (bearertype == 2) { + linkedbearer = htonl(gparsed->matched_session->defaultbearer); + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_LINKED_BEARER_ID, + sizeof(linkedbearer), (uint8_t *)&linkedbearer); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } return 0; } @@ -1737,49 +2369,78 @@ static int gtp_create_context_deactivation_iri(gtp_parsed_t *gparsed, uint32_t evtype = UMTSIRI_EVENT_TYPE_PDPCONTEXT_DEACTIVATION; gtp_infoelem_t *el; - etsili_generic_t *np; - gtp_create_pdp_generic_iri(gparsed, gparsed->matched_session, + gtp_create_umts_generic_iri(gparsed, gparsed->matched_session, params, freelist, evtype); el = gparsed->response->ies; while (el) { - uint8_t ieattr = 0; - uint8_t *attrptr = (uint8_t *)el->iecontent; - uint16_t attrlen = el->ielength; - switch(el->ietype) { - case GTPV2_IE_CAUSE: { - uint8_t smcauseval = gtpv2_cause_to_sm(attrptr); - if (smcauseval != 0) { - np = create_etsili_generic(freelist, - UMTSIRI_CONTENTS_GPRS_ERROR_CODE, - sizeof(uint8_t), &(smcauseval)); - HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), - sizeof(np->itemnum), np); - } - break; - } - case GTPV1_IE_CAUSE: { - uint8_t smcauseval = gtpv1_cause_to_sm(attrptr); - if (smcauseval != 0) { - np = create_etsili_generic(freelist, - UMTSIRI_CONTENTS_GPRS_ERROR_CODE, - sizeof(uint8_t), &(smcauseval)); - HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), - sizeof(np->itemnum), np); - } + case GTPV1_IE_CAUSE: + insert_gtp_cause_as_gprs_error(el, params, freelist, evtype); break; - } - } + el = el->next; + } - if (ieattr != 0) { - np = create_etsili_generic(freelist, ieattr, attrlen, attrptr); - HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), - np); - } + return 0; +} + +static int gtpv2_create_session_activation_failed_iri( + gtp_parsed_t *gparsed, + etsili_generic_t **params, + etsili_generic_freelist_t *freelist) { + + uint32_t evtype = EPSIRI_EVENT_TYPE_BEARER_ACTIVATION; + etsili_generic_t *np = NULL; + uint32_t bearertype = 0; + uint32_t linkedbearer = 0; + + gtp_create_eps_generic_iri(gparsed, gparsed->matched_session, + params, freelist, evtype); + /* XXX do we get a valid bearer ID in this case? */ + if (gparsed->bearerid != 255 && + gparsed->bearerid == gparsed->matched_session->defaultbearer) { + bearertype = 1; + } else if (gparsed->bearerid != 255) { + bearertype = 2; + } + + if (bearertype != 0) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_BEARER_ACTIVATION_TYPE, + sizeof(bearertype), (uint8_t *)(&bearertype)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } + + if (bearertype == 2) { + linkedbearer = htonl(gparsed->matched_session->defaultbearer); + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_LINKED_BEARER_ID, + sizeof(linkedbearer), (uint8_t *)&linkedbearer); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } + + return 0; +} + +static int gtp_create_context_activation_failed_iri(gtp_parsed_t *gparsed, + etsili_generic_t **params, etsili_generic_freelist_t *freelist) { + + uint32_t evtype = UMTSIRI_EVENT_TYPE_PDPCONTEXT_ACTIVATION; + gtp_infoelem_t *el; + + gtp_create_umts_generic_iri(gparsed, gparsed->matched_session, + params, freelist, evtype); + + el = gparsed->response->ies; + while (el) { + switch(el->ietype) { + case GTPV1_IE_CAUSE: + insert_gtp_cause_as_gprs_error(el, params, freelist, evtype); + break; + } el = el->next; } @@ -1791,19 +2452,73 @@ static int gtp_create_context_modification_iri(gtp_parsed_t *gparsed, uint32_t evtype = UMTSIRI_EVENT_TYPE_PDPCONTEXT_MODIFICATION; - gtp_create_pdp_generic_iri(gparsed, gparsed->matched_session, + gtp_create_umts_generic_iri(gparsed, gparsed->matched_session, params, freelist, evtype); return 0; } +static int gtpv2_create_session_modification_iri(gtp_parsed_t *gparsed, + etsili_generic_t **params, etsili_generic_freelist_t *freelist) { + + uint32_t evtype = EPSIRI_EVENT_TYPE_BEARER_MODIFICATION; + + gtp_create_eps_generic_iri(gparsed, gparsed->matched_session, params, + freelist, evtype); + return 0; +} + +static int gtp_create_start_with_bearer_active_iri(gtp_session_t *gsess, + etsili_generic_t **params, etsili_generic_freelist_t *freelist) { + + uint32_t evtype = EPSIRI_EVENT_TYPE_START_WITH_BEARER_ACTIVE; + + gtp_create_eps_generic_iri(NULL, gsess, params, freelist, evtype); + return 0; +} static int gtp_create_start_with_context_active_iri(gtp_session_t *gsess, etsili_generic_t **params, etsili_generic_freelist_t *freelist) { uint32_t evtype = UMTSIRI_EVENT_TYPE_START_WITH_PDPCONTEXT_ACTIVE; - gtp_create_pdp_generic_iri(NULL, gsess, params, freelist, evtype); + gtp_create_umts_generic_iri(NULL, gsess, params, freelist, evtype); + + return 0; +} + +static int gtpv2_create_session_activation_iri(gtp_parsed_t *gparsed, + etsili_generic_t **params, etsili_generic_freelist_t *freelist) { + + uint32_t evtype = EPSIRI_EVENT_TYPE_BEARER_ACTIVATION; + etsili_generic_t *np = NULL; + uint32_t linkedbearer = 0; + uint32_t bearertype = 0; + + gtp_create_eps_generic_iri(gparsed, gparsed->matched_session, + params, freelist, evtype); + + if (gparsed->bearerid != 255 && + gparsed->bearerid == gparsed->matched_session->defaultbearer) { + bearertype = 1; + } else { + bearertype = 2; + } + + if (bearertype != 0) { + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_BEARER_ACTIVATION_TYPE, + sizeof(bearertype), (uint8_t *)(&bearertype)); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } + + if (bearertype == 2) { + linkedbearer = htonl(gparsed->matched_session->defaultbearer); + np = create_etsili_generic(freelist, + EPSIRI_CONTENTS_RAW_LINKED_BEARER_ID, + sizeof(linkedbearer), (uint8_t *)&linkedbearer); + HASH_ADD_KEYPTR(hh, *params, &(np->itemnum), sizeof(np->itemnum), np); + } return 0; } @@ -1813,7 +2528,7 @@ static int gtp_create_context_activation_iri(gtp_parsed_t *gparsed, uint32_t evtype = UMTSIRI_EVENT_TYPE_PDPCONTEXT_ACTIVATION; - gtp_create_pdp_generic_iri(gparsed, gparsed->matched_session, + gtp_create_umts_generic_iri(gparsed, gparsed->matched_session, params, freelist, evtype); return 0; @@ -1827,27 +2542,69 @@ static int gtp_generate_iri_data(access_plugin_t *p UNUSED, void *parseddata, if (gparsed->action == ACCESS_ACTION_ACCEPT) { *iritype = ETSILI_IRI_BEGIN; - if (gtp_create_context_activation_iri(gparsed, params, - freelist) < 0) { + if (gparsed->version == 1 && + gtp_create_context_activation_iri(gparsed, params, + freelist) < 0) { + return -1; + } + if (gparsed->version == 2 && + gtpv2_create_session_activation_iri(gparsed, params, + freelist) < 0) { return -1; } return 0; } else if (gparsed->action == ACCESS_ACTION_REJECT) { - /* TODO need to generate a PDP context activation failed IRI */ + *iritype = ETSILI_IRI_REPORT; + if (gparsed->version == 1 && + gtp_create_context_activation_failed_iri(gparsed, params, + freelist) < 0) { + return -1; + } + if (gparsed->version == 2 && + gtpv2_create_session_activation_failed_iri(gparsed, params, + freelist) < 0) { + return -1; + } + return 0; } else if (gparsed->action == ACCESS_ACTION_INTERIM_UPDATE) { *iritype = ETSILI_IRI_CONTINUE; - if (gtp_create_context_modification_iri(gparsed, params, freelist) < 0) - { + if (gparsed->version == 1 && + gtp_create_context_modification_iri(gparsed, params, + freelist) < 0) { + return -1; + } else if (gparsed->version == 2 && + gtpv2_create_session_modification_iri(gparsed, params, + freelist) < 0) { + return -1; + } + return 0; + } + else if (gparsed->action == ACCESS_ACTION_MODIFIED) { + *iritype = ETSILI_IRI_CONTINUE; + if (gparsed->version == 1 && + gtp_create_context_modification_iri(gparsed, params, + freelist) < 0) { + return -1; + } else if (gparsed->version == 2 && + gtpv2_create_session_modification_iri(gparsed, params, + freelist) < 0) { return -1; } + return 0; } else if (gparsed->action == ACCESS_ACTION_END) { *iritype = ETSILI_IRI_END; - if (gtp_create_context_deactivation_iri(gparsed, params, - freelist) < 0) { + if (gparsed->version == 1 && + gtp_create_context_deactivation_iri(gparsed, params, + freelist) < 0) { + return -1; + } + if (gparsed->version == 2 && + gtp_create_bearer_deactivation_iri(gparsed, params, + freelist) < 0) { return -1; } return 0; @@ -1879,8 +2636,13 @@ static int gtp_generate_iri_from_session(access_plugin_t *p, if (trigger == OPENLI_IPIRI_STARTWHILEACTIVE) { *iritype = ETSILI_IRI_BEGIN; - if (gtp_create_start_with_context_active_iri(gsess, params, freelist) - < 0) { + if (gsess->gtpversion == 1 && + gtp_create_start_with_context_active_iri(gsess, params, + freelist) < 0) { + return -1; + } else if (gsess->gtpversion == 2 && + gtp_create_start_with_bearer_active_iri(gsess, params, + freelist) < 0) { return -1; } } @@ -1928,8 +2690,23 @@ static uint8_t *gtp_get_ip_contents(access_plugin_t *p UNUSED, void *parseddata, static void gtp_destroy_session_data(access_plugin_t *p UNUSED, access_session_t *sess) { - if (sess->sessionid) { - free(sess->sessionid); + gtp_global_t *glob = (gtp_global_t *)(p->plugindata); + gtp_session_t *gtpsess; + PWord_t pval; + int rc; + unsigned char altid[64]; + + JSLG(pval, glob->session_map, sess->sessionid); + if (pval != NULL) { + gtpsess = (gtp_session_t *)(*pval); + gtpsess->refcount --; + if (gtpsess->refcount <= 0) { + JSLD(rc, glob->session_map, (uint8_t *)gtpsess->sessid); + GEN_SESSID((char *)altid, gtpsess->serveripfamily, + gtpsess->serverid, gtpsess->control_teid[1]); + JSLD(rc, glob->session_map, altid); + destroy_gtp_session(gtpsess); + } } } @@ -1963,10 +2740,33 @@ static access_plugin_t gtpplugin = { gtp_get_ip_contents, }; +const char *gtp_plugin_name = "GTP"; + access_plugin_t *get_gtp_access_plugin(void) { - return >pplugin; + access_plugin_t *gtp = calloc(1, sizeof(access_plugin_t)); + + memcpy(gtp, >pplugin, sizeof(access_plugin_t)); + gtp_init_plugin_data(gtp); + return gtp; +} + +uint8_t gtp_get_parsed_version(void *parseddata) { + gtp_parsed_t *gparsed = (gtp_parsed_t *)parseddata; + + if (gparsed) { + return gparsed->version; + } + return 0; } +void destroy_gtp_access_plugin(access_plugin_t *gtp) { + if (gtp->plugindata) { + gtp_destroy_plugin_data(gtp); + } + free(gtp); +} + + // vim: set sw=4 tabstop=4 softtabstop=4 expandtab : diff --git a/src/collector/accessplugins/radius.c b/src/collector/accessplugins/radius.c index a7f1632..4b8973a 100644 --- a/src/collector/accessplugins/radius.c +++ b/src/collector/accessplugins/radius.c @@ -1638,6 +1638,7 @@ static access_session_t *radius_update_session_state(access_plugin_t *p, if (!thissess) { thissess = create_access_session(p, sessionid, 5000 - rem); thissess->cin = assign_cin(raddata); + thissess->identifier_type = OPENLI_ACCESS_SESSION_IP; HASH_ADD_KEYPTR(hh, *sesslist, thissess->sessionid, strlen(thissess->sessionid), thissess); @@ -2133,10 +2134,6 @@ static void radius_destroy_session_data(access_plugin_t *p UNUSED, Word_t rcw; radius_user_session_t *usess = (radius_user_session_t *)sess->statedata; - if (sess->sessionid) { - free(sess->sessionid); - } - if (usess == NULL) { return; } diff --git a/src/collector/collector.c b/src/collector/collector.c index 5a034c4..6b70614 100644 --- a/src/collector/collector.c +++ b/src/collector/collector.c @@ -121,6 +121,8 @@ static void log_collector_stats(collector_global_t *glob) { glob->stats.packets_sync_email); logger(LOG_INFO, "OpenLI: Packets sent to SMS workers: %lu", glob->stats.packets_sms); + logger(LOG_INFO, "OpenLI: Packets sent to GTP workers: %lu", + glob->stats.packets_gtp); logger(LOG_INFO, "OpenLI: Bad SIP packets: %lu Bad RADIUS packets: %lu", glob->stats.bad_sip_packets, glob->stats.bad_ip_session_packets); logger(LOG_INFO, "OpenLI: Records created... IPCCs: %lu IPIRIs: %lu MobIRIs: %lu", @@ -285,6 +287,24 @@ static void init_collocal(colthread_local_t *loc, collector_global_t *glob) { zmq_connect(loc->sms_worker_queues[i], pubsockname); } + loc->fromgtp_queues = calloc(glob->gtp_threads, + sizeof(libtrace_message_queue_t)); + + loc->gtp_worker_queues = calloc(glob->gtp_threads, sizeof(void *)); + for (i = 0; i < glob->gtp_threads; i++) { + char pubsockname[128]; + + snprintf(pubsockname, 128, "inproc://openligtpworker-colrecv%d", i); + loc->gtp_worker_queues[i] = zmq_socket(glob->zmq_ctxt, ZMQ_PUSH); + zmq_setsockopt(loc->gtp_worker_queues[i], ZMQ_SNDHWM, &hwm, + sizeof(hwm)); + zmq_connect(loc->gtp_worker_queues[i], pubsockname); + + libtrace_message_queue_init(&(loc->fromgtp_queues[i]), + sizeof(openli_pushed_t)); + } + loc->gtpq_count = glob->gtp_threads; + loc->fragreass = create_new_ipfrag_reassembler(); loc->tosyncq_ip = zmq_socket(glob->zmq_ctxt, ZMQ_PUSH); @@ -302,6 +322,8 @@ static void *start_processing_thread(libtrace_t *trace UNUSED, collector_global_t *glob = (collector_global_t *)global; colthread_local_t *loc = NULL; + int i; + sync_sendq_t *syncq, *sendq_hash; pthread_rwlock_wrlock(&(glob->config_mutex)); loc = glob->collocals[glob->nextloc]; @@ -314,6 +336,20 @@ static void *start_processing_thread(libtrace_t *trace UNUSED, register_sync_queues(&(glob->syncvoip), loc->tosyncq_voip, &(loc->fromsyncq_voip), t); + for (i = 0; i < glob->gtp_threads; i++) { + syncq = (sync_sendq_t *)malloc(sizeof(sync_sendq_t)); + syncq->q = &(loc->fromgtp_queues[i]); + syncq->parent = t; + + pthread_mutex_lock(&(glob->gtpworkers[i].col_queue_mutex)); + + sendq_hash = (sync_sendq_t *)(glob->gtpworkers[i].collector_queues); + HASH_ADD_PTR(sendq_hash, parent, syncq); + glob->gtpworkers[i].collector_queues = (void *)sendq_hash; + + pthread_mutex_unlock(&(glob->gtpworkers[i].col_queue_mutex)); + } + return loc; } @@ -412,6 +448,7 @@ static void stop_processing_thread(libtrace_t *trace, libtrace_thread_t *t, ipv6_target_t *v6, *tmp2; openli_pushed_t syncpush; int zero = 0, i; + sync_sendq_t *syncq, *sendq_hash; if (trace_is_err(trace)) { libtrace_err_t err = trace_get_err(trace); @@ -446,6 +483,33 @@ static void stop_processing_thread(libtrace_t *trace, libtrace_thread_t *t, zmq_close(loc->email_worker_queues[i]); } + for (i = 0; i < glob->gtp_threads; i++) { + openli_gtp_worker_t *worker; + + worker = &(glob->gtpworkers[i]); + + zmq_setsockopt(loc->gtp_worker_queues[i], ZMQ_LINGER, &zero, + sizeof(zero)); + zmq_close(loc->gtp_worker_queues[i]); + + while (libtrace_message_queue_try_get(&(loc->fromgtp_queues[i]), + (void *)&syncpush) != LIBTRACE_MQ_FAILED) { + process_incoming_messages(loc, &syncpush); + } + pthread_mutex_lock(&(worker->col_queue_mutex)); + sendq_hash = (sync_sendq_t *)(worker->collector_queues); + + HASH_FIND_PTR(sendq_hash, &t, syncq); + if (syncq) { + HASH_DELETE(hh, sendq_hash, syncq); + free(syncq); + worker->collector_queues = (void *)sendq_hash; + } + pthread_mutex_unlock(&(worker->col_queue_mutex)); + + libtrace_message_queue_destroy(&(loc->fromgtp_queues[i])); + } + for (i = 0; i < glob->sms_threads; i++) { zmq_setsockopt(loc->sms_worker_queues[i], ZMQ_LINGER, &zero, sizeof(zero)); @@ -457,9 +521,11 @@ static void stop_processing_thread(libtrace_t *trace, libtrace_thread_t *t, zmq_setsockopt(loc->tosyncq_voip, ZMQ_LINGER, &zero, sizeof(zero)); zmq_close(loc->tosyncq_voip); + free(loc->fromgtp_queues); free(loc->zmq_pubsocks); free(loc->email_worker_queues); free(loc->sms_worker_queues); + free(loc->gtp_worker_queues); HASH_ITER(hh, loc->activeipv4intercepts, v4, tmp) { free_all_ipsessions(&(v4->intercepts)); @@ -599,7 +665,6 @@ static void add_payload_info_from_packet(libtrace_packet_t *pkt, } - static void do_sms_check(colthread_local_t *loc, libtrace_packet_t *pkt, collector_global_t *glob) { @@ -810,6 +875,92 @@ static inline uint32_t is_core_server_packet( } +static uint8_t check_if_gtp(packet_info_t *pinfo, libtrace_packet_t *pkt, + colthread_local_t *loc, collector_global_t *glob) { + + uint32_t fwdto = 0; + uint8_t msgtype; + gtpv1_header_t *v1_hdr; + gtpv2_header_teid_t *v2_hdr; + + if (loc->gtpservers == NULL) { + return 0; + } + + if ( !is_core_server_packet(pinfo, loc->gtpservers)) { + return 0; + } + + add_payload_info_from_packet(pkt, pinfo); + if (pinfo->payload_len == 0) { + return 0; + } + + if (loc->gtpq_count > 1) { + /* check GTP version */ + if (((*(pinfo->payload_ptr)) & 0xe8) == 0x48) { + /* GTPv2 */ + if (pinfo->payload_len < sizeof(gtpv2_header_teid_t)) { + return 0; + } + v2_hdr = (gtpv2_header_teid_t *)pinfo->payload_ptr; + msgtype = v2_hdr->msgtype; + + /* + fwdto = hashlittle(&(v2_hdr->teid), sizeof(v2_hdr->teid), + 312267023) % loc->gtpq_count; + */ + + } else if (((*(pinfo->payload_ptr)) & 0xe0) == 0x20) { + /* GTPv1 */ + if (pinfo->payload_len < sizeof(gtpv1_header_t)) { + return 0; + } + + v1_hdr = (gtpv1_header_t *)pinfo->payload_ptr; + msgtype = v1_hdr->msgtype; + + /* + fwdto = hashlittle(&(v1_hdr->teid), sizeof(v1_hdr->teid), + 312267023) % loc->gtpq_count; + */ + + } else { + return 0; + } + + switch(msgtype) { + case GTPV1_CREATE_PDP_CONTEXT_REQUEST: + case GTPV1_UPDATE_PDP_CONTEXT_REQUEST: + case GTPV1_DELETE_PDP_CONTEXT_REQUEST: + case GTPV2_CREATE_SESSION_REQUEST: + case GTPV2_DELETE_SESSION_REQUEST: + fwdto = hashlittle(&(pinfo->srcip), + sizeof(struct sockaddr_storage), 312267023) % + loc->gtpq_count; + break; + case GTPV1_CREATE_PDP_CONTEXT_RESPONSE: + case GTPV1_UPDATE_PDP_CONTEXT_RESPONSE: + case GTPV1_DELETE_PDP_CONTEXT_RESPONSE: + case GTPV2_CREATE_SESSION_RESPONSE: + case GTPV2_DELETE_SESSION_RESPONSE: + fwdto = hashlittle(&(pinfo->destip), + sizeof(struct sockaddr_storage), 312267023) % + loc->gtpq_count; + break; + default: + fwdto = 0; + } + } + + send_packet_to_sync(pkt, loc->gtp_worker_queues[fwdto], OPENLI_UPDATE_GTP); + + pthread_mutex_lock(&(glob->stats_mutex)); + glob->stats.packets_gtp ++; + pthread_mutex_unlock(&(glob->stats_mutex)); + return 1; +} + static libtrace_packet_t *process_packet(libtrace_t *trace, libtrace_thread_t *t, void *global, void *tls, libtrace_packet_t *pkt) { @@ -820,7 +971,7 @@ static libtrace_packet_t *process_packet(libtrace_t *trace, uint16_t ethertype; uint32_t rem, iprem; uint8_t proto; - int forwarded = 0, ret; + int forwarded = 0, ret, i; int ipsynced = 0, voipsynced = 0, emailsynced = 0; uint16_t fragoff = 0; uint32_t servhash = 0; @@ -841,6 +992,14 @@ static libtrace_packet_t *process_packet(libtrace_t *trace, process_incoming_messages(loc, &syncpush); } + for (i = 0; i < loc->gtpq_count; i++) { + while (libtrace_message_queue_try_get(&(loc->fromgtp_queues[i]), + (void *)&syncpush) != LIBTRACE_MQ_FAILED) { + + process_incoming_messages(loc, &syncpush); + } + } + l3 = trace_get_layer3(pkt, ðertype, &rem); if (l3 == NULL || rem == 0) { @@ -1013,12 +1172,7 @@ static libtrace_packet_t *process_packet(libtrace_t *trace, goto processdone; } - if (loc->gtpservers && is_core_server_packet(&pinfo, - loc->gtpservers)) { - send_packet_to_sync(pkt, loc->tosyncq_ip, OPENLI_UPDATE_GTP); - ipsynced = 1; - goto processdone; - } + check_if_gtp(&pinfo, pkt, loc, glob); /* Is this a SIP packet? -- if yes, create a state update */ if (loc->sipservers && is_core_server_packet(&pinfo, @@ -1386,6 +1540,13 @@ static void destroy_collector_state(collector_global_t *glob) { free(glob->emailworkers); } + if (glob->gtpworkers) { + for (i = 0; i < glob->gtp_threads; i++) { + pthread_mutex_destroy(&(glob->gtpworkers[i].col_queue_mutex)); + } + free(glob->gtpworkers); + } + libtrace_message_queue_destroy(&(glob->intersyncq)); if (glob->zmq_encoder_ctrl) { @@ -1616,6 +1777,7 @@ static void init_collector_global(collector_global_t *glob) { glob->forwarding_threads = 1; glob->encoding_threads = 2; glob->email_threads = 1; + glob->gtp_threads = 1; glob->sms_threads = 1; glob->sharedinfo.intpointid = NULL; glob->sharedinfo.intpointid_len = 0; @@ -2240,6 +2402,10 @@ int main(int argc, char *argv[]) { pthread_setname_np(glob->smsworkers[i].threadid, name); } + glob->gtpworkers = calloc(glob->gtp_threads, sizeof(openli_gtp_worker_t)); + for (i = 0; i < glob->gtp_threads; i++) { + start_gtp_worker_thread(&(glob->gtpworkers[i]), i, glob); + } glob->emailworkers = calloc(glob->email_threads, sizeof(openli_email_worker_t)); @@ -2450,6 +2616,9 @@ int main(int argc, char *argv[]) { for (i = 0; i < glob->email_threads; i++) { pthread_join(glob->emailworkers[i].threadid, NULL); } + for (i = 0; i < glob->gtp_threads; i++) { + pthread_join(glob->gtpworkers[i].threadid, NULL); + } for (i = 0; i < glob->sms_threads; i++) { pthread_join(glob->smsworkers[i].threadid, NULL); } diff --git a/src/collector/collector.h b/src/collector/collector.h index b70a2e9..6106d36 100644 --- a/src/collector/collector.h +++ b/src/collector/collector.h @@ -52,6 +52,7 @@ #include "radius_hasher.h" #include "email_ingest_service.h" #include "email_worker.h" +#include "gtp_worker.h" #include "sms_worker.h" #include "sipparsing.h" @@ -213,12 +214,23 @@ typedef struct colthread_local { thread */ libtrace_message_queue_t fromsyncq_voip; + /* Array of message threads for receiving intercept instructions from + * the GTP processing threads + */ + libtrace_message_queue_t *fromgtp_queues; + + /* Number of GTP processing threads that have queues in the above array */ + int gtpq_count; + /* Array of message queues to pass packets to the email worker threads */ void **email_worker_queues; /* Array of message queues to pass packets to the SMS worker threads */ void **sms_worker_queues; + /* Array of message queues to pass packets to the GTP worker threads */ + void **gtp_worker_queues; + /** SIP parser for detecting SMS over SIP */ openli_sip_parser_t *sipparser; @@ -286,6 +298,7 @@ typedef struct collector_global { int encoding_threads; int forwarding_threads; int email_threads; + int gtp_threads; int sms_threads; void *zmq_encoder_ctrl; @@ -302,6 +315,7 @@ typedef struct collector_global { openli_encoder_t *encoders; forwarding_thread_data_t *forwarders; openli_email_worker_t *emailworkers; + openli_gtp_worker_t *gtpworkers; openli_sms_worker_t *smsworkers; colthread_local_t **collocals; int nextloc; diff --git a/src/collector/collector_base.h b/src/collector/collector_base.h index ba41b20..9f66031 100644 --- a/src/collector/collector_base.h +++ b/src/collector/collector_base.h @@ -85,6 +85,7 @@ typedef struct collector_stats { uint64_t packets_sync_ip; uint64_t packets_sync_voip; uint64_t packets_sync_email; + uint64_t packets_gtp; uint64_t packets_sms; uint64_t ipcc_created; uint64_t ipiri_created; diff --git a/src/collector/collector_publish.h b/src/collector/collector_publish.h index f913ec3..c234b8f 100644 --- a/src/collector/collector_publish.h +++ b/src/collector/collector_publish.h @@ -61,6 +61,8 @@ enum { OPENLI_EXPORT_EMAILIRI = 22, OPENLI_EXPORT_RAW_CC = 23, OPENLI_EXPORT_RAW_IRI = 24, + OPENLI_EXPORT_EPSCC = 25, + OPENLI_EXPORT_EPSIRI = 26, }; /* This structure is also used for IPMMCCs since they require the same diff --git a/src/collector/collector_seqtracker.c b/src/collector/collector_seqtracker.c index ce0077e..4f9add5 100644 --- a/src/collector/collector_seqtracker.c +++ b/src/collector/collector_seqtracker.c @@ -73,6 +73,7 @@ static inline char *extract_liid_from_job(openli_export_recv_t *recvd) { case OPENLI_EXPORT_IPMMIRI: return recvd->data.ipmmiri.liid; case OPENLI_EXPORT_UMTSIRI: + case OPENLI_EXPORT_EPSIRI: return recvd->data.mobiri.liid; case OPENLI_EXPORT_RAW_SYNC: case OPENLI_EXPORT_RAW_CC: @@ -98,6 +99,7 @@ static inline uint32_t extract_cin_from_job(openli_export_recv_t *recvd) { case OPENLI_EXPORT_IPMMIRI: return recvd->data.ipmmiri.cin; case OPENLI_EXPORT_UMTSIRI: + case OPENLI_EXPORT_EPSIRI: return recvd->data.mobiri.cin; case OPENLI_EXPORT_RAW_SYNC: case OPENLI_EXPORT_RAW_IRI: @@ -473,6 +475,7 @@ static void seqtracker_main(seqtracker_thread_data_t *seqdata) { case OPENLI_EXPORT_IPIRI: case OPENLI_EXPORT_UMTSCC: case OPENLI_EXPORT_UMTSIRI: + case OPENLI_EXPORT_EPSIRI: case OPENLI_EXPORT_EMAILIRI: case OPENLI_EXPORT_EMAILCC: case OPENLI_EXPORT_RAW_SYNC: diff --git a/src/collector/collector_sync.c b/src/collector/collector_sync.c index e0c2fba..fd49a9c 100644 --- a/src/collector/collector_sync.c +++ b/src/collector/collector_sync.c @@ -51,10 +51,6 @@ #include "ipiri.h" #include "collector_util.h" -#define INTERCEPT_IS_ACTIVE(cept, now) \ - (cept->common.tostart_time <= now.tv_sec && ( \ - cept->common.toend_time == 0 || cept->common.toend_time > now.tv_sec)) - collector_sync_t *init_sync_data(collector_global_t *glob) { collector_sync_t *sync = (collector_sync_t *) @@ -83,7 +79,6 @@ collector_sync_t *init_sync_data(collector_global_t *glob) { sync->upcomingtimerfd = -1; sync->radiusplugin = init_access_plugin(ACCESS_RADIUS); - sync->gtpplugin = init_access_plugin(ACCESS_GTP); sync->freegenerics = glob->syncgenericfreelist; sync->activeips = NULL; @@ -91,10 +86,12 @@ collector_sync_t *init_sync_data(collector_global_t *glob) { sync->forwardcount = glob->forwarding_threads; sync->emailcount = glob->email_threads; sync->smscount = glob->sms_threads; + sync->gtpcount = glob->gtp_threads; sync->zmq_pubsocks = calloc(sync->pubsockcount, sizeof(void *)); sync->zmq_fwdctrlsocks = calloc(sync->forwardcount, sizeof(void *)); sync->zmq_emailsocks = calloc(sync->emailcount, sizeof(void *)); + sync->zmq_gtpsocks = calloc(sync->gtpcount, sizeof(void *)); sync->zmq_smssocks = calloc(sync->smscount, sizeof(void *)); sync->ctx = glob->sslconf.ctx; @@ -114,6 +111,9 @@ collector_sync_t *init_sync_data(collector_global_t *glob) { init_zmq_socket_array(sync->zmq_emailsocks, sync->emailcount, "inproc://openliemailcontrol_sync", glob->zmq_ctxt); + init_zmq_socket_array(sync->zmq_gtpsocks, sync->gtpcount, + "inproc://openligtpcontrol_sync", glob->zmq_ctxt); + init_zmq_socket_array(sync->zmq_smssocks, sync->smscount, "inproc://openlismscontrol_sync", glob->zmq_ctxt); @@ -174,10 +174,6 @@ void clean_sync_data(collector_sync_t *sync) { destroy_access_plugin(sync->radiusplugin); } - if (sync->gtpplugin) { - destroy_access_plugin(sync->gtpplugin); - } - if(sync->ssl){ SSL_free(sync->ssl); } @@ -190,7 +186,6 @@ void clean_sync_data(collector_sync_t *sync) { sync->outgoing = NULL; sync->incoming = NULL; sync->radiusplugin = NULL; - sync->gtpplugin = NULL; sync->activeips = NULL; while (haltattempts < 10) { @@ -226,6 +221,8 @@ void clean_sync_data(collector_sync_t *sync) { sync->zmq_fwdctrlsocks, sync->forwardcount); haltfails += send_halt_message_to_zmq_socket_array( sync->zmq_emailsocks, sync->emailcount); + haltfails += send_halt_message_to_zmq_socket_array( + sync->zmq_gtpsocks, sync->gtpcount); haltfails += send_halt_message_to_zmq_socket_array( sync->zmq_smssocks, sync->smscount); @@ -238,6 +235,7 @@ void clean_sync_data(collector_sync_t *sync) { free(sync->zmq_emailsocks); free(sync->zmq_smssocks); + free(sync->zmq_gtpsocks); free(sync->zmq_pubsocks); free(sync->zmq_fwdctrlsocks); @@ -517,34 +515,6 @@ static inline void push_static_iprange_remove_to_collectors( } -static inline void push_single_ipintercept( - libtrace_message_queue_t *q, ipintercept_t *ipint, - access_session_t *session) { - - ipsession_t *ipsess; - openli_pushed_t msg; - int i; - - for (i = 0; i < session->sessipcount; i++) { - - ipsess = create_ipsession(ipint, session->cin, - session->sessionips[i].ipfamily, - (struct sockaddr *)&(session->sessionips[i].assignedip), - session->sessionips[i].prefixbits); - - if (!ipsess) { - logger(LOG_INFO, - "OpenLI: ran out of memory while creating IP session message."); - return; - } - memset(&msg, 0, sizeof(openli_pushed_t)); - msg.type = OPENLI_PUSH_IPINTERCEPT; - msg.data.ipsess = ipsess; - - libtrace_message_queue_put(q, (void *)(&msg)); - } -} - static inline void push_single_vendmirrorid(libtrace_message_queue_t *q, ipintercept_t *ipint, uint8_t msgtype) { @@ -808,24 +778,10 @@ static void push_session_update_to_threads(void *sendqs, access_session_t *sess, ipintercept_t *ipint, int updatetype) { sync_sendq_t *sendq, *tmp; - int i; - - for (i = 0; i < sess->sessipcount; i++) { - openli_pushed_t pmsg; - ipsession_t *sessdup; - - HASH_ITER(hh, (sync_sendq_t *)sendqs, sendq, tmp) { - memset(&pmsg, 0, sizeof(openli_pushed_t)); - pmsg.type = updatetype; - sessdup = create_ipsession(ipint, sess->cin, - sess->sessionips[i].ipfamily, - (struct sockaddr *)&(sess->sessionips[i].assignedip), - sess->sessionips[i].prefixbits); - - pmsg.data.ipsess = sessdup; - libtrace_message_queue_put(sendq->q, &pmsg); - } + HASH_ITER(hh, (sync_sendq_t *)sendqs, sendq, tmp) { + push_session_update_to_collector_queue(sendq->q, ipint, sess, + updatetype); } } @@ -1097,7 +1053,7 @@ static void push_existing_user_sessions(collector_sync_t *sync, HASH_ITER(hh, user->sessions, sess, tmp2) { HASH_ITER(hh, (sync_sendq_t *)(sync->glob->collector_queues), sendq, tmp) { - push_single_ipintercept(sendq->q, cept, sess); + push_session_ips_to_collector_queue(sendq->q, cept, sess); } create_iri_from_session(sync, sess, cept, @@ -1245,7 +1201,7 @@ static int update_modified_intercept(collector_sync_t *sync, add_intercept_to_user_intercept_list(&sync->userintercepts, ipint); push_existing_user_sessions(sync, ipint); - logger(LOG_INFO, "OpenLI: IP intercept %s has changed target", ipint->common.liid); + logger(LOG_INFO, "OpenLI: IP intercept %s has changed target, resuming interception for new target", ipint->common.liid); } if (ipint->vendmirrorid != modified->vendmirrorid) { @@ -1583,6 +1539,11 @@ static int recv_from_provisioner(collector_sync_t *sync) { if (ret == -1) { return -1; } + ret = forward_provmsg_to_workers(sync->zmq_gtpsocks, + sync->gtpcount, provmsg, msglen, msgtype, "GTP"); + if (ret == -1) { + return -1; + } break; case OPENLI_PROTO_ADD_STATICIPS: ret = new_staticiprange(sync, provmsg, msglen); @@ -1629,6 +1590,11 @@ static int recv_from_provisioner(collector_sync_t *sync) { if (ret == -1) { return -1; } + ret = forward_provmsg_to_workers(sync->zmq_gtpsocks, + sync->gtpcount, provmsg, msglen, msgtype, "GTP"); + if (ret == -1) { + return -1; + } break; case OPENLI_PROTO_ANNOUNCE_DEFAULT_RADIUS: ret = new_default_radius(sync, provmsg, msglen); @@ -1660,6 +1626,11 @@ static int recv_from_provisioner(collector_sync_t *sync) { if (ret < 0) { return -1; } + ret = forward_provmsg_to_workers(sync->zmq_gtpsocks, + sync->gtpcount, provmsg, msglen, msgtype, "GTP"); + if (ret == -1) { + return -1; + } break; case OPENLI_PROTO_START_VOIPINTERCEPT: @@ -1690,6 +1661,11 @@ static int recv_from_provisioner(collector_sync_t *sync) { if (ret == -1) { return -1; } + ret = forward_provmsg_to_workers(sync->zmq_gtpsocks, + sync->gtpcount, provmsg, msglen, msgtype, "GTP"); + if (ret == -1) { + return -1; + } ret = forward_provmsg_to_workers(sync->zmq_smssocks, sync->smscount, provmsg, msglen, msgtype, "SMS"); if (ret == -1) { @@ -1865,6 +1841,8 @@ void sync_disconnect_provisioner(collector_sync_t *sync, uint8_t dropmeds) { forward_provmsg_to_voipsync(sync, NULL, 0, OPENLI_PROTO_DISCONNECT); forward_provmsg_to_workers(sync->zmq_emailsocks, sync->emailcount, NULL, 0, OPENLI_PROTO_DISCONNECT, "email"); + forward_provmsg_to_workers(sync->zmq_gtpsocks, sync->gtpcount, + NULL, 0, OPENLI_PROTO_DISCONNECT, "GTP"); forward_provmsg_to_workers(sync->zmq_smssocks, sync->smscount, NULL, 0, OPENLI_PROTO_DISCONNECT, "SMS"); @@ -1899,7 +1877,7 @@ static void push_all_active_intercepts(internet_user_t *allusers, user = lookup_user_by_intercept(allusers, orig); if (user) { HASH_ITER(hh, user->sessions, sess, tmp2) { - push_single_ipintercept(q, orig, sess); + push_session_ips_to_collector_queue(q, orig, sess); } } } @@ -2148,7 +2126,7 @@ static int newly_active_session(collector_sync_t *sync, } HASH_ITER(hh, (sync_sendq_t *)(sync->glob->collector_queues), sendq, tmpq) { - push_single_ipintercept(sendq->q, ipint, sess); + push_session_ips_to_collector_queue(sendq->q, ipint, sess); } } pthread_mutex_lock(sync->glob->stats_mutex); @@ -2199,8 +2177,6 @@ static int update_user_sessions(collector_sync_t *sync, libtrace_packet_t *pkt, if (accesstype == ACCESS_RADIUS) { p = sync->radiusplugin; - } else if (accesstype == ACCESS_GTP) { - p = sync->gtpplugin; } if (!p) { diff --git a/src/collector/collector_sync.h b/src/collector/collector_sync.h index 088d7ea..841f79c 100644 --- a/src/collector/collector_sync.h +++ b/src/collector/collector_sync.h @@ -51,10 +51,12 @@ typedef struct colsync_data { int forwardcount; int emailcount; int smscount; + int gtpcount; void **zmq_pubsocks; void **zmq_fwdctrlsocks; void **zmq_emailsocks; + void **zmq_gtpsocks; void **zmq_smssocks; void *zmq_colsock; @@ -81,7 +83,6 @@ typedef struct colsync_data { wandder_encoder_t *encoder; access_plugin_t *radiusplugin; - access_plugin_t *gtpplugin; etsili_generic_freelist_t *freegenerics; ip_to_session_t *activeips; diff --git a/src/collector/encoder_worker.c b/src/collector/encoder_worker.c index 34cc62b..293f41a 100644 --- a/src/collector/encoder_worker.c +++ b/src/collector/encoder_worker.c @@ -32,6 +32,7 @@ #include "ipcc.h" #include "ipmmiri.h" #include "umtsiri.h" +#include "epsiri.h" #include "emailiri.h" #include "collector_base.h" #include "logger.h" @@ -152,7 +153,7 @@ static void free_encoded_header_templates(Pvoid_t headers) { } } -static void free_umtsiri_parameters(etsili_generic_t *params) { +static void free_mobileiri_parameters(etsili_generic_t *params) { etsili_generic_t *oldp, *tmp; @@ -481,13 +482,10 @@ static int encode_templated_emailiri(openli_encoder_t *enc, return 1; } -static int encode_templated_umtsiri(openli_encoder_t *enc, - openli_encoding_job_t *job, encoded_header_template_t *hdr_tplate, - openli_encoded_result_t *res) { +static inline void create_mobile_operator_identifier(openli_encoder_t *enc, + openli_mobiri_job_t *irijob, int elem_id) { + - wandder_encoded_result_t *body = NULL; - openli_mobiri_job_t *irijob = - (openli_mobiri_job_t *)&(job->origreq->data.mobiri); etsili_generic_t *np = NULL; char opid[6]; int opidlen; @@ -506,12 +504,74 @@ static int encode_templated_umtsiri(openli_encoder_t *enc, opid[opidlen] = '\0'; pthread_rwlock_unlock(enc->shared_mutex); - np = create_etsili_generic(enc->freegenerics, - UMTSIRI_CONTENTS_OPERATOR_IDENTIFIER, opidlen, + np = create_etsili_generic(enc->freegenerics, elem_id, opidlen, (uint8_t *)opid); HASH_ADD_KEYPTR(hh, irijob->customparams, &(np->itemnum), sizeof(np->itemnum), np); +} + +static int encode_templated_epsiri(openli_encoder_t *enc, + openli_encoding_job_t *job, encoded_header_template_t *hdr_tplate, + openli_encoded_result_t *res) { + + wandder_encoded_result_t *body = NULL; + openli_mobiri_job_t *irijob = + (openli_mobiri_job_t *)&(job->origreq->data.mobiri); + + create_mobile_operator_identifier(enc, irijob, + EPSIRI_CONTENTS_OPERATOR_IDENTIFIER); + + /* Not worth trying to template the body of EPS IRIs -- way too + * many variables in here that may or may not change on a semi-regular + * basis. + */ + reset_wandder_encoder(enc->encoder); + + body = encode_epsiri_body(enc->encoder, job->preencoded, irijob->iritype, + irijob->customparams); + + if (body == NULL || body->len == 0 || body->encoded == NULL) { + logger(LOG_INFO, "OpenLI: failed to encode ETSI EPSIRI body"); + if (body) { + wandder_release_encoded_result(enc->encoder, body); + } + return -1; + } + + if (job->encryptmethod != OPENLI_PAYLOAD_ENCRYPTION_NONE) { + if (create_encrypted_message_body(enc->encoder, &enc->encrypt, + res, hdr_tplate, + body->encoded, body->len, NULL, 0, job) < 0) { + + wandder_release_encoded_result(enc->encoder, body); + return -1; + } + } else { + if (create_etsi_encoded_result(res, hdr_tplate, body->encoded, + body->len, NULL, 0, job) < 0) { + wandder_release_encoded_result(enc->encoder, body); + return -1; + } + } + + wandder_release_encoded_result(enc->encoder, body); + free_mobileiri_parameters(irijob->customparams); + free(irijob->liid); + /* Success */ + return 1; +} + +static int encode_templated_umtsiri(openli_encoder_t *enc, + openli_encoding_job_t *job, encoded_header_template_t *hdr_tplate, + openli_encoded_result_t *res) { + + wandder_encoded_result_t *body = NULL; + openli_mobiri_job_t *irijob = + (openli_mobiri_job_t *)&(job->origreq->data.mobiri); + + create_mobile_operator_identifier(enc, irijob, + UMTSIRI_CONTENTS_OPERATOR_IDENTIFIER); /* Not worth trying to template the body of UMTS IRIs -- way too * many variables in here that may or may not change on a semi-regular * basis. @@ -548,7 +608,8 @@ static int encode_templated_umtsiri(openli_encoder_t *enc, } wandder_release_encoded_result(enc->encoder, body); - free_umtsiri_parameters(irijob->customparams); + free_mobileiri_parameters(irijob->customparams); + free(irijob->liid); /* Success */ return 1; } @@ -823,6 +884,9 @@ static int encode_etsi(openli_encoder_t *enc, openli_encoding_job_t *job, case OPENLI_EXPORT_UMTSIRI: ret = encode_templated_umtsiri(enc, job, hdr_tplate, res); break; + case OPENLI_EXPORT_EPSIRI: + ret = encode_templated_epsiri(enc, job, hdr_tplate, res); + break; case OPENLI_EXPORT_EMAILIRI: ret = encode_templated_emailiri(enc, job, hdr_tplate, res); break; diff --git a/src/collector/epsiri.h b/src/collector/epsiri.h new file mode 100644 index 0000000..06d80cd --- /dev/null +++ b/src/collector/epsiri.h @@ -0,0 +1,90 @@ +/* + * + * Copyright (c) 2024 SearchLight New Zealand. + * All rights reserved. + * + * This file is part of OpenLI. + * + * OpenLI was originally developed by the University of Waikato WAND + * research group. For further information please see http://www.wand.net.nz/ + * + * OpenLI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * OpenLI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ + +#ifndef OPENLI_EPSIRI_H_ +#define OPENLI_EPSIRI_H_ + +#include +#include +#include "collector.h" +#include "intercept.h" +#include "internetaccess.h" +#include "etsili_core.h" +#include "collector_sync.h" + +enum { + EPSIRI_CONTENTS_IMSI = 1, + EPSIRI_CONTENTS_MSISDN = 2, + EPSIRI_CONTENTS_IMEI = 3, + EPSIRI_CONTENTS_APNAME = 4, + EPSIRI_CONTENTS_PDP_ADDRESS = 7, + EPSIRI_CONTENTS_EVENT_TYPE = 8, + EPSIRI_CONTENTS_EVENT_TIME = 9, + EPSIRI_CONTENTS_LOCATION_TIME = 10, + EPSIRI_CONTENTS_GPRS_CORRELATION = 11, + EPSIRI_CONTENTS_GGSN_IPADDRESS = 14, + EPSIRI_CONTENTS_INITIATOR = 15, + EPSIRI_CONTENTS_OPERATOR_IDENTIFIER = 16, + EPSIRI_CONTENTS_PDPTYPE = 17, + EPSIRI_CONTENTS_NETWORK_ELEMENT_IPADDRESS = 18, + EPSIRI_CONTENTS_BEARER_ACTIVATION_TYPE = 19, + EPSIRI_CONTENTS_BEARER_DEACTIVATION_TYPE = 20, + /* separate the fields that are a direct copy of the IE from the + * GTPv2 header -- these go in the EPS-GTPV2-SpecificParameters + * sequence in the EPS IRI + */ + EPSIRI_CONTENTS_RAW_ULI = 101, + EPSIRI_CONTENTS_RAW_RAT_TYPE, + EPSIRI_CONTENTS_RAW_BEARER_QOS, + EPSIRI_CONTENTS_RAW_BEARER_ACTIVATION_TYPE, + EPSIRI_CONTENTS_RAW_APN_AMBR, + EPSIRI_CONTENTS_RAW_PCO_FROM_UE, + EPSIRI_CONTENTS_RAW_PCO_FROM_NETWORK, + EPSIRI_CONTENTS_RAW_BEARER_ID, + EPSIRI_CONTENTS_RAW_PROCEDURE_TRANSACTION, + EPSIRI_CONTENTS_RAW_PDN_ADDRESS_ALLOCATION, + EPSIRI_CONTENTS_RAW_PDN_TYPE, + EPSIRI_CONTENTS_RAW_FAILED_BEARER_ACTIVATION_REASON, + EPSIRI_CONTENTS_RAW_ATTACH_TYPE, + EPSIRI_CONTENTS_RAW_DETACH_TYPE, + EPSIRI_CONTENTS_RAW_LINKED_BEARER_ID, + EPSIRI_CONTENTS_RAW_FAILED_BEARER_MODIFICATION_REASON, + EPSIRI_CONTENTS_RAW_BEARER_DEACTIVATION_CAUSE, +}; + +/* Values for EPSEvent as defined in 133.108 Appendix B.9 */ +enum { + EPSIRI_EVENT_TYPE_BEARER_ACTIVATION = 18, + EPSIRI_EVENT_TYPE_START_WITH_BEARER_ACTIVE = 19, + EPSIRI_EVENT_TYPE_BEARER_MODIFICATION = 20, + EPSIRI_EVENT_TYPE_BEARER_DEACTIVATION = 21, + EPSIRI_EVENT_TYPE_UE_REQUESTED_BEARER_RESOURCE_MODIFICATION = 22, +}; + + +#endif +// vim: set sw=4 tabstop=4 softtabstop=4 expandtab : + diff --git a/src/collector/etsiencoding/encryptcontainer.c b/src/collector/etsiencoding/encryptcontainer.c index de83a46..099fcf8 100644 --- a/src/collector/etsiencoding/encryptcontainer.c +++ b/src/collector/etsiencoding/encryptcontainer.c @@ -46,6 +46,7 @@ static inline uint32_t job_origreq_to_encrypted_payload_type( return OPENLI_ENCRYPTED_PAYLOAD_TYPE_PART3; case OPENLI_EXPORT_UMTSCC: case OPENLI_EXPORT_UMTSIRI: + case OPENLI_EXPORT_EPSIRI: return OPENLI_ENCRYPTED_PAYLOAD_TYPE_PART7; case OPENLI_EXPORT_IPMMCC: case OPENLI_EXPORT_IPMMIRI: diff --git a/src/collector/etsiencoding/epsiri.c b/src/collector/etsiencoding/epsiri.c new file mode 100644 index 0000000..a22f5bf --- /dev/null +++ b/src/collector/etsiencoding/epsiri.c @@ -0,0 +1,383 @@ +/* + * + * Copyright (c) 2024 SearchLight NZ + * All rights reserved. + * + * This file is part of OpenLI. + * + * OpenLI was originally developed by the University of Waikato WAND + * research group. For further information please see http://www.wand.net.nz/ + * + * OpenLI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * OpenLI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ + +#include +#include "etsiencoding.h" +#include "logger.h" +#include "intercept.h" +#include "etsili_core.h" +#include "location.h" +#include "epsiri.h" + +wandder_encoded_result_t *encode_epsiri_body(wandder_encoder_t *encoder, + wandder_encode_job_t *precomputed, + etsili_iri_type_t iritype, etsili_generic_t *params) { + + wandder_encode_job_t *jobarray[6]; + etsili_generic_t *p, *savedtime; + uint8_t lookup; + //uint32_t iriversion = 8; + uint32_t gprstarget = 3; + + jobarray[0] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_2]); + jobarray[1] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_0]); + jobarray[2] = &(precomputed[OPENLI_PREENCODE_USEQUENCE]); + wandder_encode_next_preencoded(encoder, jobarray, 3); + + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 0, &iritype, + sizeof(iritype)); + + /* timeStamp -- as generalized time */ + lookup = EPSIRI_CONTENTS_EVENT_TIME; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_GENERALTIME, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, + p->itemptr, p->itemlen); + savedtime = p; + } else { + savedtime = NULL; + logger(LOG_INFO, + "OpenLI: warning, no timestamp available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } + + jobarray[0] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_2]); + jobarray[1] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_15]); + jobarray[2] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_0]); + + /* IRI-Parameters start here */ + + /* Object identifier (0) */ + jobarray[3] = &(precomputed[OPENLI_PREENCODE_EPSIRIOID]); + + /* LIID (1) -- fortunately the identifier matches the one + * used in the PSHeader, so we can use our preencoded + * version */ + + jobarray[4] = &(precomputed[OPENLI_PREENCODE_LIID]); + + /* timeStamp again (3) -- different format, use UTCTime */ + jobarray[5] = &(precomputed[OPENLI_PREENCODE_CSEQUENCE_3]); + wandder_encode_next_preencoded(encoder, jobarray, 6); + + if (savedtime) { + wandder_encode_next(encoder, WANDDER_TAG_UTCTIME, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, + savedtime->itemptr, savedtime->itemlen); + } + END_ENCODED_SEQUENCE(encoder, 1); + + /* initiator (4) */ + lookup = EPSIRI_CONTENTS_INITIATOR; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (!p) { + logger(LOG_INFO, + "OpenLI: warning, no initiator available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } else { + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 4, + p->itemptr, p->itemlen); + } + + /* skip locationOfTheTarget (8) because we're going to encode it later + * on inside ePS-GTPV2-specificParameters as a ULI info element */ + + /* party information (9) -- nested */ + ENC_CSEQUENCE(encoder, 9); + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 0, &gprstarget, sizeof(gprstarget)); + ENC_CSEQUENCE(encoder, 1); + + lookup = EPSIRI_CONTENTS_IMEI; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, p->itemptr, p->itemlen); + } else { + logger(LOG_INFO, + "OpenLI: warning, no IMEI available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } + + lookup = EPSIRI_CONTENTS_IMSI; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 3, p->itemptr, p->itemlen); + } else { + logger(LOG_INFO, + "OpenLI: warning, no IMSI available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } + + + lookup = EPSIRI_CONTENTS_MSISDN; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 6, p->itemptr, p->itemlen); + } else { + logger(LOG_INFO, + "OpenLI: warning, no MSISDN available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } + + END_ENCODED_SEQUENCE(encoder, 1); + + /* servicesDataInformation (pdpAddress, APN etc) */ + ENC_CSEQUENCE(encoder, 4); // services-data-information + ENC_CSEQUENCE(encoder, 1); // gprs-parameters + + + lookup = EPSIRI_CONTENTS_PDP_ADDRESS; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + ENC_CSEQUENCE(encoder, 1); // pdp-address + ENC_CSEQUENCE(encoder, 1); // datanodeaddress + encode_ipaddress(encoder, (etsili_ipaddress_t *)(p->itemptr)); + END_ENCODED_SEQUENCE(encoder, 2); + } + + lookup = EPSIRI_CONTENTS_APNAME; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 2, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_PDPTYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 3, p->itemptr, p->itemlen); + } + + END_ENCODED_SEQUENCE(encoder, 3); + + /* gprs correlation number (18) */ + lookup = EPSIRI_CONTENTS_GPRS_CORRELATION; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (!p) { + logger(LOG_INFO, + "OpenLI: warning, no GPRS correlation number available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } else { + char space[24]; + snprintf(space, 24, "%lu", *((uint64_t *)(p->itemptr))); + + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 18, space, strlen(space)); + } + + /* EPS event (20) */ + lookup = EPSIRI_CONTENTS_EVENT_TYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (!p) { + logger(LOG_INFO, + "OpenLI: warning, no EPS event type available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } else { + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 20, p->itemptr, p->itemlen); + } + + /* ggsnAddress (24) -- also appears in networkIdentifier, but why not... */ + lookup = EPSIRI_CONTENTS_GGSN_IPADDRESS; + + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + ENC_CSEQUENCE(encoder, 24); + ENC_CSEQUENCE(encoder, 1); // ipAddress + encode_ipaddress(encoder, (etsili_ipaddress_t *)(p->itemptr)); + END_ENCODED_SEQUENCE(encoder, 2); + } + + /* networkIdentifier (26) -- nested */ + + ENC_CSEQUENCE(encoder, 26); + lookup = EPSIRI_CONTENTS_OPERATOR_IDENTIFIER; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 0, p->itemptr, p->itemlen); + } else { + logger(LOG_INFO, + "OpenLI: warning, no operator identifier available for building EPS IRI"); + logger(LOG_INFO, "OpenLI: EPS IRI record may be invalid..."); + } + + lookup = EPSIRI_CONTENTS_NETWORK_ELEMENT_IPADDRESS; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + + if (p) { + ENC_CSEQUENCE(encoder, 1); + ENC_CSEQUENCE(encoder, 5); + encode_ipaddress(encoder, (etsili_ipaddress_t *)(p->itemptr)); + END_ENCODED_SEQUENCE(encoder, 2); + } + + END_ENCODED_SEQUENCE(encoder, 1); + + /* eps-GTPV2-specificParameters (36) */ + ENC_CSEQUENCE(encoder, 36); + lookup = EPSIRI_CONTENTS_RAW_PDN_ADDRESS_ALLOCATION; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_APNAME; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 2, p->itemptr, p->itemlen); + } + + /* can we have protconfigoptions from both directions at the same time? + * XXX + */ + lookup = EPSIRI_CONTENTS_RAW_PCO_FROM_UE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + ENC_CSEQUENCE(encoder, 3); + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, p->itemptr, p->itemlen); + END_ENCODED_SEQUENCE(encoder, 1); + } else { + lookup = EPSIRI_CONTENTS_RAW_PCO_FROM_NETWORK; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + ENC_CSEQUENCE(encoder, 3); + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 2, p->itemptr, p->itemlen); + END_ENCODED_SEQUENCE(encoder, 1); + } + } + + /* Attach Type goes here... */ + + lookup = EPSIRI_CONTENTS_RAW_BEARER_ID; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 5, p->itemptr, p->itemlen); + } + + + /* Detach Type goes here... */ + + lookup = EPSIRI_CONTENTS_RAW_RAT_TYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 7, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_RAW_FAILED_BEARER_ACTIVATION_REASON; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 8, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_RAW_BEARER_QOS; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 9, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_BEARER_ACTIVATION_TYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 10, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_RAW_APN_AMBR; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 11, p->itemptr, p->itemlen); + } + + /* procedureTransactionID goes here */ + + lookup = EPSIRI_CONTENTS_RAW_LINKED_BEARER_ID; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 13, p->itemptr, p->itemlen); + } + + /* TFT goes here */ + + /* Handover Indication */ + + lookup = EPSIRI_CONTENTS_RAW_FAILED_BEARER_MODIFICATION_REASON; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 16, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_BEARER_DEACTIVATION_TYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_ENUM, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 21, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_RAW_BEARER_DEACTIVATION_CAUSE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 22, p->itemptr, p->itemlen); + } + + lookup = EPSIRI_CONTENTS_RAW_ULI; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + ENC_CSEQUENCE(encoder, 23); + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 1, p->itemptr, p->itemlen); + END_ENCODED_SEQUENCE(encoder, 1); + } + + lookup = EPSIRI_CONTENTS_RAW_PDN_TYPE; + HASH_FIND(hh, params, &lookup, sizeof(lookup), p); + if (p) { + wandder_encode_next(encoder, WANDDER_TAG_OCTETSTRING, + WANDDER_CLASS_CONTEXT_PRIMITIVE, 24, p->itemptr, p->itemlen); + } + + END_ENCODED_SEQUENCE(encoder, 8); + return wandder_encode_finish(encoder); +} diff --git a/src/collector/etsiencoding/etsiencoding.c b/src/collector/etsiencoding/etsiencoding.c index aa39ed0..0ba0dae 100644 --- a/src/collector/etsiencoding/etsiencoding.c +++ b/src/collector/etsiencoding/etsiencoding.c @@ -171,6 +171,7 @@ int create_etsi_encoded_result(openli_encoded_result_t *res, case OPENLI_EXPORT_IPIRI: case OPENLI_EXPORT_IPMMIRI: case OPENLI_EXPORT_UMTSIRI: + case OPENLI_EXPORT_EPSIRI: case OPENLI_EXPORT_EMAILIRI: res->header.intercepttype = htons(OPENLI_PROTO_ETSI_IRI); break; diff --git a/src/collector/gtp.h b/src/collector/gtp.h new file mode 100644 index 0000000..1647c6d --- /dev/null +++ b/src/collector/gtp.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2024 Searchlight New Zealand Ltd. + * All rights reserved. + * + * This file is part of OpenLI. + * + * OpenLI was originally developed by the University of Waikato WAND + * research group. For further information please see http://www.wand.net.nz/ + * + * OpenLI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * OpenLI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ +#ifndef OPENLI_GTP_H_ +#define OPENLI_GTP_H_ + +/* Need these GTP header definitions in multiple places, so they go in + * here rather than being confined to the GTP plugin... + */ +typedef struct gtpv1_header { + uint8_t octet1; + uint8_t msgtype; + uint16_t msglen; + uint32_t teid; + uint16_t seqno; + uint8_t npdu; + uint8_t next_ext; +} PACKED gtpv1_header_t; + +typedef struct gtpv2_header_teid { + uint8_t octet1; + uint8_t msgtype; + uint16_t msglen; + uint32_t teid; + uint32_t seqno; +} PACKED gtpv2_header_teid_t; + +enum { + GTPV1_CREATE_PDP_CONTEXT_REQUEST = 16, + GTPV1_CREATE_PDP_CONTEXT_RESPONSE = 17, + GTPV1_UPDATE_PDP_CONTEXT_REQUEST = 18, + GTPV1_UPDATE_PDP_CONTEXT_RESPONSE = 19, + GTPV1_DELETE_PDP_CONTEXT_REQUEST = 20, + GTPV1_DELETE_PDP_CONTEXT_RESPONSE = 21, + + GTPV2_CREATE_SESSION_REQUEST = 32, + GTPV2_CREATE_SESSION_RESPONSE = 33, + GTPV2_MODIFY_BEARER_REQUEST = 34, + GTPV2_MODIFY_BEARER_RESPONSE = 35, + GTPV2_DELETE_SESSION_REQUEST = 36, + GTPV2_DELETE_SESSION_RESPONSE = 37, + GTPV2_MODIFY_BEARER_COMMAND = 64, + GTPV2_MODIFY_BEARER_FAILURE_INDICATION = 65, + GTPV2_DELETE_BEARER_COMMAND = 66, + GTPV2_DELETE_BEARER_FAILURE_INDICATION = 67, + GTPV2_CREATE_BEARER_REQUEST = 95, + GTPV2_CREATE_BEARER_RESPONSE = 96, + GTPV2_UPDATE_BEARER_REQUEST = 97, + GTPV2_UPDATE_BEARER_RESPONSE = 98, + GTPV2_DELETE_BEARER_REQUEST = 99, + GTPV2_DELETE_BEARER_RESPONSE = 100, +}; + +uint8_t gtp_get_parsed_version(void *parseddata); + +#endif diff --git a/src/collector/gtp_worker.c b/src/collector/gtp_worker.c new file mode 100644 index 0000000..6c3db61 --- /dev/null +++ b/src/collector/gtp_worker.c @@ -0,0 +1,970 @@ +/* + * + * Copyright (c) 2024 Searchlight New Zealand Ltd. + * All rights reserved. + * + * This file is part of OpenLI. + * + * OpenLI was originally developed by the University of Waikato WAND + * research group. For further information please see http://www.wand.net.nz/ + * + * OpenLI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * OpenLI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include "gtp_worker.h" +#include "collector.h" +#include "logger.h" +#include "util.h" +#include "intercept.h" +#include "netcomms.h" + +static void remove_gtp_intercept(openli_gtp_worker_t *worker, + ipintercept_t *ipint) { + + /* The sync thread should tell the collector threads that the intercept + * is over, so we don't need to "withdraw" any IP sessions that we've + * announced. + */ + + remove_intercept_from_user_intercept_list(&worker->userintercepts, ipint); + HASH_DELETE(hh_liid, worker->ipintercepts, ipint); + free_single_ipintercept(ipint); +} + +static void disable_unconfirmed_gtp_intercepts(openli_gtp_worker_t *worker) { + ipintercept_t *ipint, *tmp; + + HASH_ITER(hh_liid, worker->ipintercepts, ipint, tmp) { + if (ipint->awaitingconfirm) { + remove_gtp_intercept(worker, ipint); + } + } +} + +static void flag_gtp_intercepts(ipintercept_t *cepts) { + ipintercept_t *ipint, *tmp; + + /* Don't worry about statics, because we should not be dealing with + * them here anyway */ + HASH_ITER(hh_liid, cepts, ipint, tmp) { + ipint->awaitingconfirm = 1; + } + +} + +static void push_existing_ip_sessions(openli_gtp_worker_t *worker UNUSED, + ipintercept_t *ipint UNUSED) { + + /* TODO */ +} + +static int init_gtp_intercept(openli_gtp_worker_t *worker, + ipintercept_t *ipint) { + + if (ipint->accesstype != INTERNET_ACCESS_TYPE_MOBILE) { + /* Only care about "mobile" intercepts */ + free_single_ipintercept(ipint); + return 0; + } + + /* Discard any static IPs announced for this intercept, as they are + * irrelevant for the purposes of this thread. + */ + free_all_staticipranges(&(ipint->statics)); + ipint->statics = NULL; + + if (worker->tracker_threads <= 1) { + ipint->common.seqtrackerid = 0; + } else { + ipint->common.seqtrackerid = hash_liid(ipint->common.liid) % + worker->tracker_threads; + } + + add_intercept_to_user_intercept_list(&worker->userintercepts, ipint); + HASH_ADD_KEYPTR(hh_liid, worker->ipintercepts, ipint->common.liid, + ipint->common.liid_len, ipint); + ipint->awaitingconfirm = 0; + return 1; +} + +static void update_modified_gtp_intercept(openli_gtp_worker_t *worker, + ipintercept_t *found, ipintercept_t *ipint) { + + int changed = 0; + + if (ipint->accesstype != INTERNET_ACCESS_TYPE_MOBILE) { + /* Intercept has changed to be NOT mobile, so just remove it */ + remove_intercept_from_user_intercept_list(&worker->userintercepts, + found); + HASH_DELETE(hh_liid, worker->ipintercepts, found); + free_single_ipintercept(ipint); + free_single_ipintercept(found); + return; + } + + update_modified_intercept_common(&(found->common), &(ipint->common), + OPENLI_INTERCEPT_TYPE_IP, &changed); + if (strcmp(ipint->username, found->username) != 0 || + ipint->mobileident != found->mobileident) { + remove_intercept_from_user_intercept_list(&worker->userintercepts, + found); + free(found->username); + found->username = ipint->username; + found->username_len = ipint->username_len; + found->mobileident = ipint->mobileident; + ipint->username = NULL; + add_intercept_to_user_intercept_list(&worker->userintercepts, + found); + + push_existing_ip_sessions(worker, found); + } + found->awaitingconfirm = 0; + free_single_ipintercept(ipint); +} + +static int add_new_gtp_intercept(openli_gtp_worker_t *worker, + provisioner_msg_t *msg) { + + ipintercept_t *ipint, *found; + int ret = 0; + + ipint = calloc(1, sizeof(ipintercept_t)); + if (decode_ipintercept_start(msg->msgbody, msg->msglen, ipint) < 0) { + logger(LOG_INFO, "OpenLI: GTP worker %d failed to decode mobile IP intercept start message from provisioner", worker->workerid); + return -1; + } + + HASH_FIND(hh_liid, worker->ipintercepts, ipint->common.liid, + ipint->common.liid_len, found); + + if (found) { + update_modified_gtp_intercept(worker, found, ipint); + found->awaitingconfirm = 0; + ret = 0; + } else { + ret = init_gtp_intercept(worker, ipint); + } + return ret; +} + +static int modify_gtp_intercept(openli_gtp_worker_t *worker, + provisioner_msg_t *msg) { + ipintercept_t *ipint, *found; + + ipint = calloc(1, sizeof(ipintercept_t)); + if (decode_ipintercept_modify(msg->msgbody, msg->msglen, ipint) < 0) { + logger(LOG_INFO, "OpenLI: GTP worker %d failed to decode mobile IP intercept modify message from provisioner", worker->workerid); + return -1; + } + + HASH_FIND(hh_liid, worker->ipintercepts, ipint->common.liid, + ipint->common.liid_len, found); + if (!found) { + return init_gtp_intercept(worker, ipint); + } else { + update_modified_gtp_intercept(worker, found, ipint); + } + return 0; +} + +static int halt_gtp_intercept(openli_gtp_worker_t *worker, + provisioner_msg_t *msg) { + ipintercept_t *ipint, *found; + + ipint = calloc(1, sizeof(ipintercept_t)); + if (decode_ipintercept_halt(msg->msgbody, msg->msglen, ipint) < 0) { + logger(LOG_INFO, "OpenLI: GTP worker %d failed to decode mobile IP intercept halt message from provisioner", worker->workerid); + return -1; + } + + HASH_FIND(hh_liid, worker->ipintercepts, ipint->common.liid, + ipint->common.liid_len, found); + if (found) { + remove_gtp_intercept(worker, found); + } + free_single_ipintercept(ipint); + return 0; +} + +static int gtp_worker_handle_provisioner_message(openli_gtp_worker_t *worker, + openli_export_recv_t *msg) { + + int ret = 0; + switch(msg->data.provmsg.msgtype) { + case OPENLI_PROTO_NOMORE_INTERCEPTS: + disable_unconfirmed_gtp_intercepts(worker); + break; + case OPENLI_PROTO_DISCONNECT: + flag_gtp_intercepts(worker->ipintercepts); + break; + case OPENLI_PROTO_START_IPINTERCEPT: + ret = add_new_gtp_intercept(worker, &(msg->data.provmsg)); + break; + case OPENLI_PROTO_HALT_IPINTERCEPT: + ret = halt_gtp_intercept(worker, &(msg->data.provmsg)); + break; + case OPENLI_PROTO_MODIFY_IPINTERCEPT: + ret = modify_gtp_intercept(worker, &(msg->data.provmsg)); + break; + default: + logger(LOG_INFO, "OpenLI: GTP worker thread %d received unexpected message type from provisioner: %u", + worker->workerid, msg->data.provmsg.msgtype); + ret = -1; + } + + if (msg->data.provmsg.msgbody) { + free(msg->data.provmsg.msgbody); + } + return ret; +} + +static int gtp_worker_process_sync_thread_message(openli_gtp_worker_t *worker) { + + openli_export_recv_t *msg; + int x; + + do { + x = zmq_recv(worker->zmq_ii_sock, &msg, sizeof(msg), ZMQ_DONTWAIT); + if (x < 0 && errno != EAGAIN) { + logger(LOG_INFO, + "OpenLI: error while receiving II in GTP thread %d: %s", + worker->workerid, strerror(errno)); + return -1; + } + + if (x <= 0) { + break; + } + + if (msg->type == OPENLI_EXPORT_HALT) { + free(msg); + return -1; + } + + if (msg->type == OPENLI_EXPORT_PROVISIONER_MESSAGE) { + if (gtp_worker_handle_provisioner_message(worker, msg) < 0) { + free(msg); + return -1; + } + } + + free(msg); + } while (x > 0); + + return 1; +} + +static inline internet_user_t *lookup_gtp_userid(openli_gtp_worker_t *worker, + user_identity_t *userid) { + + internet_user_t *iuser; + + iuser = lookup_user_by_identity(worker->allusers, userid); + + if (iuser == NULL) { + iuser = (internet_user_t *)malloc(sizeof(internet_user_t)); + if (!iuser) { + logger(LOG_INFO, + "OpenLI: unable to allocate memory for new Internet user in GTP worker %d", + worker->workerid); + return NULL; + } + iuser->userid = NULL; + iuser->sessions = NULL; + + add_userid_to_allusers_map(&(worker->allusers), iuser, userid); + } + return iuser; +} + +static void push_gtp_session_over(openli_gtp_worker_t *worker, + user_intercept_list_t *userint, access_session_t *sess) { + + ipintercept_t *ipint, *tmp; + sync_sendq_t *sendq, *tmpq, *queues; + + queues = (sync_sendq_t *)worker->collector_queues; + + if (userint == NULL || sess == NULL ) { + return; + } + + /* For each intercept associated with this user identity, tell + * all of the collector threads to stop intercepting traffic for + * the IP address(es) that belongs to that session. + */ + HASH_ITER(hh_user, userint->intlist, ipint, tmp) { + HASH_ITER(hh, queues, sendq, tmpq) { + push_session_update_to_collector_queue(sendq->q, ipint, sess, + OPENLI_PUSH_HALT_IPINTERCEPT); + } + } +} + +static void add_teid_to_session_mapping(openli_gtp_worker_t *worker, + access_session_t *sess, uint32_t teid, internet_user_t *iuser) { + + teid_to_session_t *found; + char keystr[1024]; + + memset(keystr, 0, 1024); + + if (teid == 0) { + logger(LOG_INFO, "OpenLI: called add_teid_to_session_mapping() but the assigned TEID is zero for the session?"); + return; + } + snprintf(keystr, 1024, "%s-%u", sess->gtp_tunnel_endpoints, teid); + + //printf("TEID ID: %s\n", keystr); + + HASH_FIND(hh, worker->all_data_teids, keystr, strlen(keystr), found); + if (found && found->cin == sess->cin) { + found->session = realloc(found->session, + (found->sessioncount + 1) * sizeof(access_session_t *)); + found->owner = realloc(found->owner, + (found->sessioncount + 1) * sizeof(internet_user_t *)); + found->session[found->sessioncount] = sess; + found->owner[found->sessioncount] = iuser; + found->sessioncount ++; + return; + } else if (found) { + /* a silent log-off scenario? XXX do we need to generate an IRI? */ + + /* For now, just delete the old entry and fall through... */ + HASH_DELETE(hh, worker->all_data_teids, found); + free(found->idstring); + free(found->session); + free(found->owner); + free(found); + } + + found = calloc(1, sizeof(teid_to_session_t)); + found->idstring = strdup(keystr); + found->teid = teid; + found->cin = sess->cin; + found->sessioncount = 1; + found->session = calloc(1, sizeof(access_session_t *)); + found->owner = calloc(1, sizeof(internet_user_t *)); + found->session[0] = sess; + found->owner[0] = iuser; + + HASH_ADD_KEYPTR(hh, worker->all_data_teids, &(found->teid), + sizeof(found->teid), found); +} + +static void remove_teid_to_session_mapping(openli_gtp_worker_t *worker, + access_session_t *sess, uint32_t teid) { + + teid_to_session_t *found; + int nullsess = 0, i; + char keystr[1024]; + + if (!sess->teids_mapped) { + return; + } + snprintf(keystr, 1024, "%s-%u", sess->gtp_tunnel_endpoints, teid); + //printf("DELETING %s\n", keystr); + + HASH_FIND(hh, worker->all_data_teids, keystr, strlen(keystr), found); + if (!found) { + /* Weird, but ok we'll just ignore this */ + return; + } + + for (i = 0; i < found->sessioncount; i++) { + if (found->session[i] == NULL) { + nullsess ++; + continue; + } + if (found->session[i] == sess) { + found->session[i] = NULL; + found->owner[i] = NULL; + nullsess ++; + } + } + if (nullsess == found->sessioncount) { + /* All sessions relating to this TEID have been removed, so + * free the entire object + */ + HASH_DELETE(hh, worker->all_data_teids, found); + free(found->idstring); + free(found->session); + free(found->owner); + free(found); + } + +} + +static void newly_active_gtp_session(openli_gtp_worker_t *worker, + user_intercept_list_t *userint, access_session_t *sess, + internet_user_t *iuser) { + + ipintercept_t *ipint, *tmp; + sync_sendq_t *sendq, *tmpq; + + if (userint == NULL || sess == NULL) { + return; + } + + /* Save the Data TEIDs for this session as we have to now + * intercept GTP-U for those TEIDs from now on -- TODO + */ + if (sess->identifier_type & OPENLI_ACCESS_SESSION_TEID) { + add_teid_to_session_mapping(worker, sess, sess->teids[0], iuser); + add_teid_to_session_mapping(worker, sess, sess->teids[1], iuser); + sess->teids_mapped = 1; + } + + if (sess->sessipcount == 0) { + return; + } + + /* Tell the collector threads about any IPs associated with this + * newly active session. + */ + HASH_ITER(hh_user, userint->intlist, ipint, tmp) { + HASH_ITER(hh, (sync_sendq_t *)(worker->collector_queues), sendq, + tmpq) { + push_session_ips_to_collector_queue(sendq->q, ipint, sess); + } + } + +} + +static void export_raw_gtp_c_packet_content(openli_gtp_worker_t *worker UNUSED, + ipintercept_t *ipint UNUSED, void *parseddata UNUSED, + uint32_t seqno UNUSED, uint32_t cin UNUSED) { + + /* Generate a RAW IRI encoding job for each GTP-C packet that contributed + * to the current GTP "action", so that pcapdisk intercepts can + * write them into the pcap file nicely */ + + /* TODO */ + +} + +static void create_iri_from_gtp_action(openli_gtp_worker_t *worker, + ipintercept_t *ipint, access_session_t *sess, void *parseddata) { + + struct timeval now; + access_plugin_t *p = worker->gtpplugin; + openli_export_recv_t *irimsg; + int tracker = ipint->common.seqtrackerid; + int ret; + + if (ipint->common.tomediate == OPENLI_INTERCEPT_OUTPUTS_CCONLY) { + return; + } + + gettimeofday(&now, NULL); + if (!INTERCEPT_IS_ACTIVE(ipint, now)) { + return; + } + + irimsg = (openli_export_recv_t *)calloc(1, sizeof(openli_export_recv_t)); + irimsg->destid = ipint->common.destid; + irimsg->data.mobiri.liid = strdup(ipint->common.liid); + irimsg->data.mobiri.cin = sess->cin; + irimsg->data.mobiri.iritype = ETSILI_IRI_NONE; + irimsg->data.mobiri.customparams = NULL; + + if (gtp_get_parsed_version(parseddata) == 1) { + irimsg->type = OPENLI_EXPORT_UMTSIRI; + } else { + irimsg->type = OPENLI_EXPORT_EPSIRI; + } + + ret = p->generate_iri_data(p, parseddata, + &(irimsg->data.mobiri.customparams), + &(irimsg->data.mobiri.iritype), worker->freegenerics, 0); + if (ret == -1) { + logger(LOG_INFO, + "OpenLI: error while creating IRI from GTP session state change for %s (worker=%d)", irimsg->data.mobiri.liid, worker->workerid); + free(irimsg->data.mobiri.liid); + free(irimsg); + return; + } + + if (irimsg->data.mobiri.iritype == ETSILI_IRI_NONE) { + free(irimsg->data.mobiri.liid); + free(irimsg); + return; + } + pthread_mutex_lock(worker->stats_mutex); + worker->stats->mobiri_created ++; + pthread_mutex_unlock(worker->stats_mutex); + publish_openli_msg(worker->zmq_pubsocks[tracker], irimsg); +} + +static void generate_encoding_jobs(openli_gtp_worker_t *worker, + user_intercept_list_t *userint, access_session_t *sess, + void *parseddata, access_action_t action) { + + ipintercept_t *ipint, *tmp; + access_plugin_t *p = worker->gtpplugin; + + if (userint == NULL) { + return; + } + + HASH_ITER(hh_user, userint->intlist, ipint, tmp) { + if (ipint->common.targetagency == NULL || + strcmp(ipint->common.targetagency, "pcapdisk") == 0) { + uint32_t seqno; + seqno = p->get_packet_sequence(p, parseddata); + + export_raw_gtp_c_packet_content(worker, ipint, parseddata, + seqno, sess->cin); + } else if (action != ACCESS_ACTION_NONE) { + create_iri_from_gtp_action(worker, ipint, sess, parseddata); + } + } +} + +static void process_gtp_u_packet(openli_gtp_worker_t *worker UNUSED, + libtrace_packet_t *packet, uint8_t *payload UNUSED, + uint32_t plen UNUSED, uint32_t teid) { + + void *l3; + uint16_t ethertype; + uint32_t rem; + + char keystr[1024]; + + l3 = trace_get_layer3(packet, ðertype, &rem); + if (l3 == NULL || rem < sizeof(libtrace_ip_t)) { + return; + } + if (ethertype == TRACE_ETHERTYPE_IP) { + libtrace_ip_t *ip = (libtrace_ip_t *)l3; + + if (ip->ip_src.s_addr < ip->ip_dst.s_addr) { + snprintf(keystr, 1024, "%u-%u-%u", ip->ip_src.s_addr, + ip->ip_dst.s_addr, teid); + } else { + snprintf(keystr, 1024, "%u-%u-%u", ip->ip_dst.s_addr, + ip->ip_src.s_addr, teid); + } + } else if (ethertype == TRACE_ETHERTYPE_IPV6) { + libtrace_ip6_t *ip6 = (libtrace_ip6_t *)l3; + if (rem < sizeof(libtrace_ip6_t)) { + return; + } + if (memcmp(&(ip6->ip_src.s6_addr), &(ip6->ip_dst.s6_addr), 16) < 0) { + snprintf(keystr, 1024, "%lu-%lu-%lu-%lu-%u", + *(uint64_t *)(&(ip6->ip_src.s6_addr)), + *(uint64_t *)(&(ip6->ip_src.s6_addr[8])), + *(uint64_t *)(&(ip6->ip_dst.s6_addr)), + *(uint64_t *)(&(ip6->ip_dst.s6_addr[8])), + teid); + } else { + snprintf(keystr, 1024, "%lu-%lu-%lu-%lu-%u", + *(uint64_t *)(&(ip6->ip_dst.s6_addr)), + *(uint64_t *)(&(ip6->ip_dst.s6_addr[8])), + *(uint64_t *)(&(ip6->ip_src.s6_addr)), + *(uint64_t *)(&(ip6->ip_src.s6_addr[8])), + teid); + } + } else { + return; + } + + //printf("GTP-U: lookup for %s\n", keystr); + +} + +static void process_gtp_c_packet(openli_gtp_worker_t *worker, + libtrace_packet_t *packet) { + + access_plugin_t *p = worker->gtpplugin; + void *parseddata; + user_identity_t *identities = NULL; + int useridcnt = 0, i; + internet_user_t *iuser; + access_session_t *sess = NULL; + session_state_t oldstate, newstate; + access_action_t accessaction; + user_intercept_list_t *userint; + + parseddata = p->process_packet(p, packet); + if (parseddata == NULL) { + logger(LOG_INFO, + "OpenLI: GTP worker %d was unable to parse GTP-C packet", + worker->workerid); + pthread_mutex_lock(worker->stats_mutex); + worker->stats->bad_ip_session_packets ++; + pthread_mutex_unlock(worker->stats_mutex); + return; + } + + identities = p->get_userid(p, parseddata, &useridcnt); + if (identities == NULL) { + goto end_gtpc_processing; + } + + oldstate = SESSION_STATE_NEW; + newstate = SESSION_STATE_NEW; + + for (i = 0; i < useridcnt; i++) { + iuser = lookup_gtp_userid(worker, &identities[i]); + + if (iuser == NULL) { + break; + } + sess = p->update_session_state(p, parseddata, identities[i].plugindata, + &(iuser->sessions), &oldstate, &newstate, &accessaction); + if (sess == NULL) { + /* Unable to match packet to a session, ignore it */ + continue; + } + + HASH_FIND(hh, worker->userintercepts, iuser->userid, + strlen(iuser->userid), userint); + + if (oldstate != newstate) { + if (newstate == SESSION_STATE_ACTIVE) { + newly_active_gtp_session(worker, userint, sess, iuser); + + } else if (newstate == SESSION_STATE_OVER) { + push_gtp_session_over(worker, userint, sess); + remove_teid_to_session_mapping(worker, sess, sess->teids[0]); + remove_teid_to_session_mapping(worker, sess, sess->teids[1]); + } + } + + generate_encoding_jobs(worker, userint, sess, parseddata, accessaction); + + if (oldstate != newstate && newstate == SESSION_STATE_OVER) { + /* TODO remove data TEID from list of intercepted TEIDs */ + HASH_DELETE(hh, iuser->sessions, sess); + free_single_session(sess); + } + } + +end_gtpc_processing: + if (parseddata) { + p->destroy_parsed_data(p, parseddata); + } + + if (identities) { + for (i = 0; i < useridcnt; i++) { + if (identities[i].idstr) { + free(identities[i].idstr); + } + } + free(identities); + } + +} + +static void process_gtp_packet(openli_gtp_worker_t *worker, + libtrace_packet_t *packet) { + uint8_t *payload; + uint32_t plen; + uint8_t proto; + uint32_t rem; + void *transport; + uint8_t msgtype; + uint32_t teid; + + if (packet == NULL) { + return; + } + + transport = trace_get_transport(packet, &proto, &rem); + if (transport == NULL || rem == 0) { + return; + } + + plen = trace_get_payload_length(packet); + if (proto != TRACE_IPPROTO_UDP) { + /* should be UDP only */ + return; + } + payload = (uint8_t *)trace_get_payload_from_udp((libtrace_udp_t *)transport, + &rem); + if (rem < plen) { + plen = rem; + } + + if (((*payload) & 0xe8) == 0x48) { + /* GTPv2 */ + gtpv2_header_teid_t *v2hdr = (gtpv2_header_teid_t *)payload; + + if (plen <= sizeof(gtpv2_header_teid_t)) { + return; + } + + msgtype = v2hdr->msgtype; + teid = v2hdr->teid; + payload += sizeof(gtpv2_header_teid_t); + plen -= sizeof(gtpv2_header_teid_t); + + } else if (((*payload) & 0xe0) == 0x20) { + /* GTPv1 */ + gtpv1_header_t *v1hdr = (gtpv1_header_t *)payload; + + if (plen <= sizeof(gtpv1_header_t)) { + return; + } + + msgtype = v1hdr->msgtype; + teid = v1hdr->teid; + payload += sizeof(gtpv1_header_t); + plen -= sizeof(gtpv1_header_t); + } else { + return; + } + + if (msgtype == 0xff) { + /* This is GTP-U */ + process_gtp_u_packet(worker, packet, payload, plen, teid); + } else { + /* This is GTP-C */ + process_gtp_c_packet(worker, packet); + } +} + +static int gtp_worker_process_packet(openli_gtp_worker_t *worker) { + openli_state_update_t recvd; + int rc; + + do { + rc = zmq_recv(worker->zmq_colthread_recvsock, &recvd, sizeof(recvd), + ZMQ_DONTWAIT); + if (rc < 0) { + if (errno == EAGAIN) { + return 0; + } + logger(LOG_INFO, + "OpenLI: error while receiving packet in SMS worker thread %d: %s", + worker->workerid, strerror(errno)); + return -1; + } + + if (recvd.type != OPENLI_UPDATE_GTP) { + logger(LOG_INFO, + "OpenLI: GTP worker thread %d received unexpected update type %u", + worker->workerid, recvd.type); + break; + } + + process_gtp_packet(worker, recvd.data.pkt); + + if (recvd.data.pkt) { + trace_destroy_packet(recvd.data.pkt); + } + } while (rc > 0); + + return 0; +} + +static void gtp_worker_main(openli_gtp_worker_t *worker) { + + zmq_pollitem_t *topoll; + sync_epoll_t purgetimer; + struct itimerspec its; + int x; + + logger(LOG_INFO, "OpenLI: starting GTP worker thread %d", + worker->workerid); + + topoll = calloc(3, sizeof(zmq_pollitem_t)); + + its.it_value.tv_sec = 60; + its.it_value.tv_nsec = 0; + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + + purgetimer.fdtype = 0; + purgetimer.fd = timerfd_create(CLOCK_MONOTONIC, 0); + timerfd_settime(purgetimer.fd, 0, &its, NULL); + + while (1) { + topoll[0].socket = worker->zmq_ii_sock; + topoll[0].events = ZMQ_POLLIN; + + topoll[1].socket = worker->zmq_colthread_recvsock; + topoll[1].events = ZMQ_POLLIN; + + topoll[2].socket = NULL; + topoll[2].fd = purgetimer.fd; + topoll[2].events = ZMQ_POLLIN; + + if ((x = zmq_poll(topoll, 3, 50)) < 0) { + if (errno == EINTR) { + continue; + } + logger(LOG_INFO, + "OpenLI: error while polling in GTP worker thread %d: %s", + worker->workerid, strerror(errno)); + break; + } + + if (x == 0) { + continue; + } + + if (topoll[0].revents & ZMQ_POLLIN) { + x = gtp_worker_process_sync_thread_message(worker); + if (x < 0) { + break; + } + topoll[0].revents = 0; + } + + if (topoll[1].revents & ZMQ_POLLIN) { + x = gtp_worker_process_packet(worker); + if (x < 0) { + break; + } + topoll[1].revents = 0; + } + + if (topoll[2].revents & ZMQ_POLLIN) { + topoll[2].revents = 0; + close(topoll[2].fd); + + /* TODO purge "inactive" sessions */ + + purgetimer.fdtype = 0; + purgetimer.fd = timerfd_create(CLOCK_MONOTONIC, 0); + timerfd_settime(purgetimer.fd, 0, &its, NULL); + + topoll[2].fd = purgetimer.fd; + } + } + + free(topoll); +} + +void *gtp_thread_begin(void *arg) { + openli_gtp_worker_t *worker = (openli_gtp_worker_t *)arg; + char sockname[256]; + int zero = 0, x; + openli_state_update_t recvd; + teid_to_session_t *iter, *tmp; + + worker->zmq_pubsocks = calloc(worker->tracker_threads, sizeof(void *)); + init_zmq_socket_array(worker->zmq_pubsocks, worker->tracker_threads, + "inproc://openlipub", worker->zmq_ctxt); + + worker->zmq_ii_sock = zmq_socket(worker->zmq_ctxt, ZMQ_PULL); + snprintf(sockname, 256, "inproc://openligtpcontrol_sync-%d", + worker->workerid); + + if (zmq_bind(worker->zmq_ii_sock, sockname) < 0) { + logger(LOG_INFO, "OpenLI: GTP processing thread %d failed to bind to II zmq: %s", worker->workerid, strerror(errno)); + goto haltgtpworker; + } + + if (zmq_setsockopt(worker->zmq_ii_sock, ZMQ_LINGER, &zero, sizeof(zero)) + != 0) { + logger(LOG_INFO, "OpenLI: GTP processing thread %d failed to configure II zmq: %s", worker->workerid, strerror(errno)); + goto haltgtpworker; + } + + worker->zmq_colthread_recvsock = zmq_socket(worker->zmq_ctxt, ZMQ_PULL); + snprintf(sockname, 256, "inproc://openligtpworker-colrecv%d", + worker->workerid); + + if (zmq_bind(worker->zmq_colthread_recvsock, sockname) < 0) { + logger(LOG_INFO, "OpenLI: GTP processing thread %d failed to bind to colthread zmq: %s", worker->workerid, strerror(errno)); + goto haltgtpworker; + } + + if (zmq_setsockopt(worker->zmq_colthread_recvsock, ZMQ_LINGER, &zero, + sizeof(zero)) != 0) { + logger(LOG_INFO, "OpenLI: GTP processing thread %d failed to configure colthread zmq: %s", worker->workerid, strerror(errno)); + goto haltgtpworker; + } + + gtp_worker_main(worker); + + do { + x = zmq_recv(worker->zmq_colthread_recvsock, &recvd, sizeof(recvd), + ZMQ_DONTWAIT); + if (x > 0) { + trace_destroy_packet(recvd.data.pkt); + } + } while (x > 0); + +haltgtpworker: + logger(LOG_INFO, "OpenLI: halting GTP processing thread %d", + worker->workerid); + + HASH_ITER(hh, worker->all_data_teids, iter, tmp) { + HASH_DELETE(hh, worker->all_data_teids, iter); + free(iter->idstring); + free(iter->session); + free(iter->owner); + free(iter); + } + + zmq_close(worker->zmq_ii_sock); + zmq_close(worker->zmq_colthread_recvsock); + free_all_users(worker->allusers); + clear_user_intercept_list(worker->userintercepts); + free_all_ipintercepts(&(worker->ipintercepts)); + clear_zmq_socket_array(worker->zmq_pubsocks, worker->tracker_threads); + free_etsili_generics(worker->freegenerics); + + if (worker->gtpplugin) { + destroy_gtp_access_plugin(worker->gtpplugin); + } + + pthread_exit(NULL); +} + +int start_gtp_worker_thread(openli_gtp_worker_t *worker, int id, + void *globarg) { + collector_global_t *glob = (collector_global_t *)globarg; + char name[1024]; + + snprintf(name, 1024, "gtpworker-%d", id); + + pthread_mutex_init(&(worker->col_queue_mutex), NULL); + + worker->zmq_ctxt = glob->zmq_ctxt; + worker->workerid = id; + worker->stats_mutex = &(glob->stats_mutex); + worker->stats = &(glob->stats); + worker->shared = &(glob->sharedinfo); + worker->zmq_ii_sock = NULL; + worker->zmq_pubsocks = NULL; + worker->zmq_colthread_recvsock = NULL; + worker->collector_queues = NULL; + worker->tracker_threads = glob->seqtracker_threads; + worker->ipintercepts = NULL; + worker->allusers = NULL; + worker->all_data_teids = NULL; + worker->userintercepts = NULL; + worker->gtpplugin = get_gtp_access_plugin(); + worker->freegenerics = create_etsili_generic_freelist(1); + + pthread_create(&(worker->threadid), NULL, gtp_thread_begin, + (void *)worker); + pthread_setname_np(worker->threadid, name); + + return 1; +} + diff --git a/src/collector/gtp_worker.h b/src/collector/gtp_worker.h new file mode 100644 index 0000000..10881df --- /dev/null +++ b/src/collector/gtp_worker.h @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) 2024 Searchlight New Zealand Ltd. + * All rights reserved. + * + * This file is part of OpenLI. + * + * OpenLI was originally developed by the University of Waikato WAND + * research group. For further information please see http://www.wand.net.nz/ + * + * OpenLI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * OpenLI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ + +#ifndef OPENLI_GTP_WORKER_H_ +#define OPENLI_GTP_WORKER_H_ + +#include "gtp.h" +#include "intercept.h" +#include "collector_base.h" +#include "collector_util.h" +#include "internetaccess.h" + +typedef struct openli_gtp_worker { + /* The global zeromq context for the entire program */ + void *zmq_ctxt; + + /* A sequential identifier for this worker thread */ + int workerid; + + /* Mutex to protect the collector stats from races */ + pthread_mutex_t *stats_mutex; + + /* Collector statistics (e.g. CC and IRI counters since starting) */ + collector_stats_t *stats; + + /* Shared global-level configuration for this collector instance */ + collector_identity_t *shared; + + /* RW mutex to protect the shared config against race conditions */ + pthread_rwlock_t *shared_mutex; + + /* ZMQ for receiving instructions from sync thread */ + void *zmq_ii_sock; + + /* ZMQs for publishing to seqtracker threads */ + void **zmq_pubsocks; + + /* ZMQ for receiving from collector threads */ + void *zmq_colthread_recvsock; + + /* Hash map of send_syncq_t instances that are used to push interceptable + * IP addresses back to the collector threads */ + void *collector_queues; + + /* Mutex to protect the collector_queues map */ + pthread_mutex_t col_queue_mutex; + + /* Number of sequence tracker threads operated by this collector */ + int tracker_threads; + + /* The pthread ID for this worker thread */ + pthread_t threadid; + + /* Set of all mobile IP data intercepts announced to this collector */ + ipintercept_t *ipintercepts; + + /* Set of all "users" (i.e. MSISDNs, IMSIs, IMEIs) with active GTP + * sessions */ + internet_user_t *allusers; + + /* Set of all data TEIDs for active intercepts */ + teid_to_session_t *all_data_teids; + + /* Map of user identities -> active intercepts */ + user_intercept_list_t *userintercepts; + + /* Instance of the GTP session state processing plugin used to + * track sessions observed in GTP-C traffic + */ + access_plugin_t *gtpplugin; + + /* Free list of ETSILI generic IEs that have been used for encoding + * fields in previous ETSI records by this thread instance. + */ + etsili_generic_freelist_t *freegenerics; + +} openli_gtp_worker_t; + +int start_gtp_worker_thread(openli_gtp_worker_t *worker, int id, + void *globarg); + +#endif diff --git a/src/collector/internetaccess.c b/src/collector/internetaccess.c index 1c4a647..21bb445 100644 --- a/src/collector/internetaccess.c +++ b/src/collector/internetaccess.c @@ -27,6 +27,7 @@ #include "logger.h" #include "util.h" #include "internetaccess.h" +#include "collector.h" access_plugin_t *init_access_plugin(uint8_t accessmethod) { @@ -55,6 +56,54 @@ void destroy_access_plugin(access_plugin_t *p) { p->destroy_plugin_data(p); } + +int push_session_ips_to_collector_queue(libtrace_message_queue_t *q, + ipintercept_t *ipint, access_session_t *session) { + + ipsession_t *ipsess; + openli_pushed_t msg; + int i; + + for (i = 0; i < session->sessipcount; i++) { + + ipsess = create_ipsession(ipint, session->cin, + session->sessionips[i].ipfamily, + (struct sockaddr *)&(session->sessionips[i].assignedip), + session->sessionips[i].prefixbits); + + if (!ipsess) { + logger(LOG_INFO, + "OpenLI: ran out of memory while creating IP session message."); + return -1; + } + memset(&msg, 0, sizeof(openli_pushed_t)); + msg.type = OPENLI_PUSH_IPINTERCEPT; + msg.data.ipsess = ipsess; + + libtrace_message_queue_put(q, (void *)(&msg)); + } + return session->sessipcount; +} + +void push_session_update_to_collector_queue(libtrace_message_queue_t *q, + ipintercept_t *ipint, access_session_t *sess, int updatetype) { + + openli_pushed_t pmsg; + int i; + ipsession_t *sessdup; + + for (i = 0; i < sess->sessipcount; i++) { + memset(&pmsg, 0, sizeof(openli_pushed_t)); + pmsg.type = updatetype; + sessdup = create_ipsession(ipint, sess->cin, + sess->sessionips[i].ipfamily, + (struct sockaddr *)&(sess->sessionips[i].assignedip), + sess->sessionips[i].prefixbits); + pmsg.data.ipsess = sessdup; + libtrace_message_queue_put(q, &pmsg); + } +} + static inline void free_session(access_session_t *sess) { if (sess == NULL) { @@ -65,9 +114,15 @@ static inline void free_session(access_session_t *sess) { if (sess->plugin) { sess->plugin->destroy_session_data(sess->plugin, sess); } + if (sess->gtp_tunnel_endpoints) { + free(sess->gtp_tunnel_endpoints); + } if (sess->sessionips) { free(sess->sessionips); } + if (sess->sessionid) { + free(sess->sessionid); + } free(sess); } @@ -176,6 +231,7 @@ access_session_t *create_access_session(access_plugin_t *p, char *sessid, newsess = (access_session_t *)malloc(sizeof(access_session_t)); + newsess->identifier_type = OPENLI_ACCESS_SESSION_UNKNOWN; newsess->plugin = p; newsess->sessionid = fast_strdup(sessid, sessid_len); newsess->statedata = NULL; @@ -190,13 +246,17 @@ access_session_t *create_access_session(access_plugin_t *p, char *sessid, newsess->started.tv_sec = 0; newsess->started.tv_usec = 0; + newsess->teids[0] = 0; + newsess->teids[1] = 0; + newsess->teids_mapped = 0; + newsess->gtp_tunnel_endpoints = NULL; return newsess; } void add_new_session_ip(access_session_t *sess, void *att_val, int family, uint8_t pfxbits, int att_len) { - int ind = sess->sessipcount; + int ind = sess->sessipcount; if (sess->sessipcount > 0 && (sess->sessipcount % SESSION_IP_INCR) == 0) { sess->sessionips = realloc(sess->sessionips, @@ -256,6 +316,7 @@ void add_new_session_ip(access_session_t *sess, void *att_val, sess->sessionips[ind].ipfamily = family; sess->sessionips[ind].prefixbits = pfxbits; sess->sessipcount ++; + sess->identifier_type |= OPENLI_ACCESS_SESSION_IP; } int free_single_session(access_session_t *sess) { diff --git a/src/collector/internetaccess.h b/src/collector/internetaccess.h index d19da36..3a60438 100644 --- a/src/collector/internetaccess.h +++ b/src/collector/internetaccess.h @@ -109,8 +109,26 @@ typedef struct ip_to_session { UT_hash_handle hh; } ip_to_session_t; +typedef struct teid_to_session { + char *idstring; + uint32_t teid; + int sessioncount; + access_session_t **session; + internet_user_t **owner; + uint32_t cin; + UT_hash_handle hh; +} teid_to_session_t; + +typedef enum { + OPENLI_ACCESS_SESSION_UNKNOWN = 0, + OPENLI_ACCESS_SESSION_IP = 1, + OPENLI_ACCESS_SESSION_TEID = 2, +} openli_access_session_id_t; + struct access_session { + openli_access_session_id_t identifier_type; + internetaccess_ip_t *sessionips; uint8_t sessipcount; session_ipversion_t sessipversion; @@ -125,6 +143,10 @@ struct access_session { struct timeval started; + char *gtp_tunnel_endpoints; + uint32_t teids[2]; + uint8_t teids_mapped; + UT_hash_handle hh; } ; @@ -192,12 +214,17 @@ int free_single_session(access_session_t *sess); access_plugin_t *get_radius_access_plugin(void); access_plugin_t *get_gtp_access_plugin(void); +void destroy_gtp_access_plugin(access_plugin_t *gtp); access_session_t *create_access_session(access_plugin_t *p, char *idstr, int idstr_len); void add_new_session_ip(access_session_t *sess, void *att_val, int family, uint8_t pfxbits, int att_len); int remove_session_ip(access_session_t *sess, internetaccess_ip_t *sessip); +int push_session_ips_to_collector_queue(libtrace_message_queue_t *q, + ipintercept_t *ipint, access_session_t *session); +void push_session_update_to_collector_queue(libtrace_message_queue_t *q, + ipintercept_t *ipint, access_session_t *sess, int updatetype); const char *accesstype_to_string(internet_access_method_t am); diff --git a/src/configparser.c b/src/configparser.c index 02c4b27..c74e2e4 100644 --- a/src/configparser.c +++ b/src/configparser.c @@ -345,6 +345,29 @@ static void parse_email_targets(email_target_t **targets, yaml_document_t *doc, } +static void parse_col_thread_count(int *toset, const char *expectedkey, + yaml_node_t *key, yaml_node_t *value, const char *errlabel) { + + if (key->type != YAML_SCALAR_NODE) { + return; + } + if (value->type != YAML_SCALAR_NODE) { + return; + } + + if (strcmp(expectedkey, (const char *)key->data.scalar.value) != 0) { + return; + } + + *toset = strtoul((const char *)value->data.scalar.value, NULL, 10); + if (*toset <= 0) { + *toset = 1; + logger(LOG_INFO, + "OpenLI: must have at least one %s thread per collector!", + errlabel); + } +} + static void parse_sip_targets(libtrace_list_t *targets, yaml_document_t *doc, yaml_node_t *tgtconf) { @@ -1284,38 +1307,18 @@ static int global_parser(void *arg, yaml_document_t *doc, } } - if (key->type == YAML_SCALAR_NODE && - value->type == YAML_SCALAR_NODE && - strcmp((char *)key->data.scalar.value, "seqtrackerthreads") == 0) { - glob->seqtracker_threads = strtoul((char *) value->data.scalar.value, - NULL, 10); - if (glob->seqtracker_threads <= 0) { - glob->seqtracker_threads = 1; - logger(LOG_INFO, "OpenLI: must have at least one sequence tracker thread per collector!"); - } - } - - if (key->type == YAML_SCALAR_NODE && - value->type == YAML_SCALAR_NODE && - strcmp((char *)key->data.scalar.value, "encoderthreads") == 0) { - glob->encoding_threads = strtoul((char *) value->data.scalar.value, - NULL, 10); - if (glob->encoding_threads <= 0) { - glob->encoding_threads = 1; - logger(LOG_INFO, "OpenLI: must have at least one encoder thread per collector!"); - } - } - - if (key->type == YAML_SCALAR_NODE && - value->type == YAML_SCALAR_NODE && - strcmp((char *)key->data.scalar.value, "forwardingthreads") == 0) { - glob->forwarding_threads = strtoul((char *) value->data.scalar.value, - NULL, 10); - if (glob->forwarding_threads <= 0) { - glob->forwarding_threads = 1; - logger(LOG_INFO, "OpenLI: must have at least one forwarding thread per collector!"); - } - } + parse_col_thread_count(&(glob->encoding_threads), "seqtrackerthreads", + key, value, "sequence tracker"); + parse_col_thread_count(&(glob->encoding_threads), "encoderthreads", + key, value, "encoder"); + parse_col_thread_count(&(glob->forwarding_threads), "forwardingthreads", + key, value, "forwarding"); + parse_col_thread_count(&(glob->email_threads), "emailthreads", + key, value, "email worker"); + parse_col_thread_count(&(glob->sms_threads), "smsthreads", + key, value, "SMS worker"); + parse_col_thread_count(&(glob->gtp_threads), "gtpthreads", + key, value, "GTP worker"); if (key->type == YAML_SCALAR_NODE && value->type == YAML_SCALAR_NODE && diff --git a/src/etsili_core.c b/src/etsili_core.c index 7d5c21b..612b7be 100644 --- a/src/etsili_core.c +++ b/src/etsili_core.c @@ -42,6 +42,7 @@ uint8_t etsi_emailirioid[4] = {0x05, 0x02, 0x0f, 0x01}; uint8_t etsi_emailccoid[4] = {0x05, 0x02, 0x0f, 0x02}; uint8_t etsi_umtsirioid[9] = {0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x01, 0x0f, 0x05}; uint8_t etsi_hi1operationoid[8] = {0x00, 0x04, 0x00, 0x02, 0x02, 0x00, 0x01, 0x06}; +uint8_t etsi_epsirioid[9] = {0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x08, 0x11, 0x00}; static inline void encode_tri_body(wandder_encoder_t *encoder) { ENC_CSEQUENCE(encoder, 2); // Payload @@ -114,7 +115,7 @@ static inline void encode_hi1_notification_body(wandder_encoder_t *encoder, wandder_encode_endseq(encoder); // End Outermost Sequence } -wandder_encoded_result_t *encode_umtscc_body(wandder_encoder_t *encoder, +void encode_umtscc_body(wandder_encoder_t *encoder, wandder_encode_job_t *precomputed, void *ipcontent, uint32_t iplen, uint8_t dir) { @@ -151,7 +152,6 @@ wandder_encoded_result_t *encode_umtscc_body(wandder_encoder_t *encoder, wandder_encode_next(encoder, WANDDER_TAG_IPPACKET, WANDDER_CLASS_CONTEXT_PRIMITIVE, 4, ipcontent, iplen); END_ENCODED_SEQUENCE(encoder, 4); - return wandder_encode_finish(encoder); } static inline void encode_emailcc_body(wandder_encoder_t *encoder, @@ -456,10 +456,6 @@ wandder_encoded_result_t *encode_umtsiri_body(wandder_encoder_t *encoder, ENC_CSEQUENCE(encoder, 1); // datanodeaddress encode_ipaddress(encoder, (etsili_ipaddress_t *)(p->itemptr)); END_ENCODED_SEQUENCE(encoder, 2); - } else { - logger(LOG_INFO, - "OpenLI: warning, no PDP Address available for constructing UMTS IRI"); - logger(LOG_INFO, "OpenLI: UMTS IRI record may be invalid..."); } /* TODO figure out if we need to include the "length" field in our @@ -1161,6 +1157,13 @@ void etsili_preencode_static_fields( p->valspace = NULL; p->vallen = 0; + p = &(pendarray[OPENLI_PREENCODE_CSEQUENCE_15]); + p->identclass = WANDDER_CLASS_CONTEXT_CONSTRUCT; + p->identifier = 15; + p->encodeas = WANDDER_TAG_SEQUENCE; + p->valspace = NULL; + p->vallen = 0; + p = &(pendarray[OPENLI_PREENCODE_PSDOMAINID]); p->identclass = WANDDER_CLASS_CONTEXT_PRIMITIVE; p->identifier = 0; @@ -1258,6 +1261,12 @@ void etsili_preencode_static_fields( p->encodeas = WANDDER_TAG_OID; wandder_encode_preencoded_value(p, etsi_umtsirioid, sizeof(etsi_umtsirioid)); + p = &(pendarray[OPENLI_PREENCODE_EPSIRIOID]); + p->identclass = WANDDER_CLASS_CONTEXT_PRIMITIVE; + p->identifier = 0; + p->encodeas = WANDDER_TAG_OID; + wandder_encode_preencoded_value(p, etsi_epsirioid, sizeof(etsi_epsirioid)); + p = &(pendarray[OPENLI_PREENCODE_IPMMCCOID]); p->identclass = WANDDER_CLASS_CONTEXT_PRIMITIVE; p->identifier = 0; diff --git a/src/etsili_core.h b/src/etsili_core.h index 4ae927f..c0ac155 100644 --- a/src/etsili_core.h +++ b/src/etsili_core.h @@ -172,6 +172,7 @@ typedef enum { OPENLI_PREENCODE_CSEQUENCE_7, /* Microsecond timestamp */ OPENLI_PREENCODE_CSEQUENCE_11, /* IPMMIRI */ OPENLI_PREENCODE_CSEQUENCE_12, /* IPMMCC */ + OPENLI_PREENCODE_CSEQUENCE_15, /* EPSIRI */ OPENLI_PREENCODE_PSDOMAINID, OPENLI_PREENCODE_LIID, OPENLI_PREENCODE_AUTHCC, @@ -184,6 +185,7 @@ typedef enum { OPENLI_PREENCODE_IPCCOID, OPENLI_PREENCODE_IPIRIOID, OPENLI_PREENCODE_UMTSIRIOID, + OPENLI_PREENCODE_EPSIRIOID, OPENLI_PREENCODE_EMAILIRIOID, OPENLI_PREENCODE_EMAILCCOID, OPENLI_PREENCODE_IPMMCCOID, @@ -247,10 +249,6 @@ uint8_t DERIVE_INTEGER_LENGTH(uint64_t x); int calculate_pspdu_length(uint32_t contentsize); -wandder_encoded_result_t *encode_umtscc_body(wandder_encoder_t *encoder, - wandder_encode_job_t *precomputed, void *ipcontent, uint32_t iplen, - uint8_t dir); - wandder_encoded_result_t *encode_ipiri_body(wandder_encoder_t *encoder, wandder_encode_job_t *precomputed, etsili_iri_type_t iritype, etsili_generic_t **params); @@ -259,6 +257,10 @@ wandder_encoded_result_t *encode_umtsiri_body(wandder_encoder_t *encoder, wandder_encode_job_t *precomputed, etsili_iri_type_t iritype, etsili_generic_t *params); +wandder_encoded_result_t *encode_epsiri_body(wandder_encoder_t *encoder, + wandder_encode_job_t *precomputed, + etsili_iri_type_t iritype, etsili_generic_t *params); + wandder_encoded_result_t *encode_emailiri_body(wandder_encoder_t *encoder, wandder_encode_job_t *precomputed, etsili_iri_type_t iritype, etsili_generic_t **params); diff --git a/src/intercept.c b/src/intercept.c index 5417cd4..db9c52f 100644 --- a/src/intercept.c +++ b/src/intercept.c @@ -403,19 +403,24 @@ void free_single_emailintercept(emailintercept_t *m) { free(m); } -void free_single_ipintercept(ipintercept_t *cept) { +void free_all_staticipranges(static_ipranges_t **ipranges) { static_ipranges_t *ipr, *tmp; + HASH_ITER(hh, *ipranges, ipr, tmp) { + HASH_DELETE(hh, *ipranges, ipr); + free_single_staticiprange(ipr); + } + *ipranges = NULL; +} + +void free_single_ipintercept(ipintercept_t *cept) { + free_intercept_common(&(cept->common)); if (cept->username) { free(cept->username); } - HASH_ITER(hh, cept->statics, ipr, tmp) { - HASH_DELETE(hh, cept->statics, ipr); - free_single_staticiprange(ipr); - } - + free_all_staticipranges(&(cept->statics)); free(cept); } diff --git a/src/intercept.h b/src/intercept.h index 0634f3f..9e64a65 100644 --- a/src/intercept.h +++ b/src/intercept.h @@ -36,6 +36,10 @@ #define OPENLI_VENDOR_MIRROR_NONE (0xffffffff) +#define INTERCEPT_IS_ACTIVE(cept, now) \ + (cept->common.tostart_time <= now.tv_sec && ( \ + cept->common.toend_time == 0 || cept->common.toend_time > now.tv_sec)) + typedef enum { OPENLI_INTERCEPT_TYPE_UNKNOWN = 0, OPENLI_INTERCEPT_TYPE_IP = 1, @@ -436,6 +440,7 @@ void free_all_rtpstreams(rtpstreaminf_t **streams); void free_all_ipsessions(ipsession_t **sessions); void free_all_vendmirror_intercepts(vendmirror_intercept_list_t **mirror_intercepts); void free_all_staticipsessions(staticipsession_t **statintercepts); +void free_all_staticipranges(static_ipranges_t **ipranges); void free_voip_cinmap(voipcinmap_t *cins); void free_single_ipintercept(ipintercept_t *cept); diff --git a/src/provisioner/hup_reload.c b/src/provisioner/hup_reload.c index b0ca115..9629974 100644 --- a/src/provisioner/hup_reload.c +++ b/src/provisioner/hup_reload.c @@ -256,9 +256,11 @@ static void remove_withdrawn_intercept(provision_state_t *currstate, if (!droppedmeds) { announce_hi1_notification_to_mediators(currstate, common, target_info, HI1_LI_DEACTIVATED); - if (target_info) { - free(target_info); - } + } + + if (common->local) { + free(common->local); + common->local = NULL; } logger(LOG_INFO, "OpenLI provisioner: LIID %s has been withdrawn following a config reload.", @@ -612,7 +614,6 @@ static int reload_ipintercepts(provision_state_t *currstate, } remove_withdrawn_intercept(currstate, &(ipint->common), ipint->username, droppedmeds); - continue; } else { int staticchanged = reload_staticips(currstate, ipint, newequiv);