diff --git a/src/pgw/pgw-context.c b/src/pgw/pgw-context.c index 8d3df28087..eb022b4b3f 100644 --- a/src/pgw/pgw-context.c +++ b/src/pgw/pgw-context.c @@ -79,6 +79,8 @@ void pgw_context_init(void) ogs_pool_init(&pgw_pf_pool, ogs_config()->pool.pf); + self.sess_hash = ogs_hash_make(); + ogs_list_init(&self.sess_list); context_initiaized = 1; @@ -93,6 +95,9 @@ void pgw_context_final(void) pgw_dev_remove_all(); pgw_subnet_remove_all(); + ogs_assert(self.sess_hash); + ogs_hash_destroy(self.sess_hash); + ogs_pool_final(&pgw_bearer_pool); ogs_pool_final(&pgw_sess_pool); ogs_pool_final(&pgw_pf_pool); @@ -707,6 +712,16 @@ int pgw_context_parse_config(void) return OGS_OK; } +static void *sess_hash_keygen(uint8_t *out, int *out_len, + uint8_t *imsi, int imsi_len, char *apn) +{ + memcpy(out, imsi, imsi_len); + ogs_cpystrn((char*)(out+imsi_len), apn, OGS_MAX_APN_LEN+1); + *out_len = imsi_len+strlen((char*)(out+imsi_len)); + + return out; +} + pgw_sess_t *pgw_sess_add( uint8_t *imsi, int imsi_len, char *apn, uint8_t pdn_type, uint8_t ebi) @@ -779,6 +794,11 @@ pgw_sess_t *pgw_sess_add( sess->ipv4 ? INET_NTOP(&sess->ipv4->addr, buf1) : "", sess->ipv6 ? INET6_NTOP(&sess->ipv6->addr, buf2) : ""); + /* Generate Hash Key : IMSI + APN */ + sess_hash_keygen(sess->hash_keybuf, &sess->hash_keylen, + imsi, imsi_len, apn); + ogs_hash_set(self.sess_hash, sess->hash_keybuf, sess->hash_keylen, sess); + ogs_list_add(&self.sess_list, sess); stats_add_session(); @@ -792,6 +812,8 @@ int pgw_sess_remove(pgw_sess_t *sess) ogs_list_remove(&self.sess_list, sess); + ogs_hash_set(self.sess_hash, sess->hash_keybuf, sess->hash_keylen, NULL); + if (sess->ipv4) pgw_ue_ip_free(sess->ipv4); if (sess->ipv6) @@ -825,6 +847,18 @@ pgw_sess_t *pgw_sess_find_by_teid(uint32_t teid) return pgw_sess_find(teid); } +pgw_sess_t *pgw_sess_find_by_imsi_apn( + uint8_t *imsi, int imsi_len, char *apn) +{ + uint8_t keybuf[OGS_MAX_IMSI_LEN+OGS_MAX_APN_LEN+1]; + int keylen = 0; + + ogs_assert(self.sess_hash); + + sess_hash_keygen(keybuf, &keylen, imsi, imsi_len, apn); + return (pgw_sess_t *)ogs_hash_get(self.sess_hash, keybuf, keylen); +} + ogs_gtp_node_t *pgw_sgw_add_by_message(ogs_gtp_message_t *message) { int rv; @@ -890,10 +924,31 @@ pgw_sess_t *pgw_sess_add_by_message(ogs_gtp_message_t *message) apn, req->pdn_type.u8, req->bearer_contexts_to_be_created.eps_bearer_id.u8); - sess = pgw_sess_add(req->imsi.data, req->imsi.len, apn, - req->pdn_type.u8, - req->bearer_contexts_to_be_created.eps_bearer_id.u8); - ogs_assert(sess); + /* + * 3GPP TS 29.274 Release 15, Page 38 + * + * If the new Create Session Request received by the PGW collides with + * an existing PDN connection context (the existing PDN connection context + * is identified with the triplet [IMSI, EPS Bearer ID, Interface type], + * where applicable Interface type here is S2a TWAN GTP-C interface or + * S2b ePDG GTP-C interface or S5/S8 SGW GTP-C interface, and where IMSI + * shall be replaced by TAC and SNR part of ME Identity for emergency + * attached UE without UICC or authenticated IMSI), this Create Session + * Request shall be treated as a request for a new session. Before creating + * the new session, the PGW should delete: + * + * - the existing PDN connection context, if the Create Session Request + * collides with the default bearer of an existing PDN connection context; + * - the existing dedicated bearer context, if the Create Session Request + * collides with a dedicated bearer of an existing PDN connection context. + */ + sess = pgw_sess_find_by_imsi_apn(req->imsi.data, req->imsi.len, apn); + if (!sess) { + sess = pgw_sess_add(req->imsi.data, req->imsi.len, apn, + req->pdn_type.u8, + req->bearer_contexts_to_be_created.eps_bearer_id.u8); + ogs_assert(sess); + } return sess; } diff --git a/src/pgw/pgw-context.h b/src/pgw/pgw-context.h index 1dd95864b7..bd63018b51 100644 --- a/src/pgw/pgw-context.h +++ b/src/pgw/pgw-context.h @@ -87,6 +87,8 @@ typedef struct pgw_context_s { ogs_list_t sgw_s5u_list; /* SGW GTPU Node List */ ogs_list_t ip_pool_list; + ogs_hash_t *sess_hash; /* hash table (IMSI+APN) */ + ogs_list_t sess_list; } pgw_context_t; @@ -146,6 +148,9 @@ typedef struct pgw_sess_s { ogs_tai_t tai; ogs_e_cgi_t e_cgi; + uint8_t hash_keybuf[OGS_MAX_IMSI_LEN+OGS_MAX_APN_LEN+1]; + int hash_keylen; + ogs_list_t bearer_list; /* Related Context */ @@ -229,6 +234,7 @@ int pgw_sess_remove(pgw_sess_t *sess); void pgw_sess_remove_all(void); pgw_sess_t *pgw_sess_find(uint32_t index); pgw_sess_t *pgw_sess_find_by_teid(uint32_t teid); +pgw_sess_t *pgw_sess_find_by_imsi_apn(uint8_t *imsi, int imsi_len, char *apn); pgw_bearer_t *pgw_bearer_add(pgw_sess_t *sess); int pgw_bearer_remove(pgw_bearer_t *bearer); diff --git a/src/sgw/sgw-context.c b/src/sgw/sgw-context.c index 27d26c3b73..8644e607f4 100644 --- a/src/sgw/sgw-context.c +++ b/src/sgw/sgw-context.c @@ -57,6 +57,8 @@ void sgw_context_init(void) ogs_pool_init(&sgw_bearer_pool, ogs_config()->pool.bearer); ogs_pool_init(&sgw_tunnel_pool, ogs_config()->pool.tunnel); + self.imsi_ue_hash = ogs_hash_make(); + ogs_list_init(&self.sgw_ue_list); context_initialized = 1; @@ -68,6 +70,9 @@ void sgw_context_final(void) sgw_ue_remove_all(); + ogs_assert(self.imsi_ue_hash); + ogs_hash_destroy(self.imsi_ue_hash); + ogs_pool_final(&sgw_tunnel_pool); ogs_pool_final(&sgw_bearer_pool); ogs_pool_final(&sgw_sess_pool); @@ -435,8 +440,31 @@ sgw_ue_t *sgw_ue_add_by_message(ogs_gtp_message_t *message) ogs_trace("sgw_ue_add_by_message() - IMSI "); ogs_log_hexdump(OGS_LOG_TRACE, req->imsi.data, req->imsi.len); - sgw_ue = sgw_ue_add(req->imsi.data, req->imsi.len); - ogs_assert(sgw_ue); + /* + * 3GPP TS 29.274 Release 15, Page 38 + * + * If the new Create Session Request received by the SGW collides with + * an existing active PDN connection context (the existing PDN connection + * context is identified with the tuple [IMSI, EPS Bearer ID], where IMSI + * shall be replaced by TAC and SNR part of ME Identity for emergency + * attached UE without UICC or authenticated IMSI), this Create Session + * Request shall be treated as a request for a new session. Before creating + * the new session, the SGW should delete: + * + * - the existing PDN connection context locally, if the Create Session + * Request is received with the TEID set to zero in the header, or + * if it is received with a TEID not set to zero in the header and + * it collides with the default bearer of an existing PDN connection + * context; + * - the existing dedicated bearer context locally, if the Create Session + * Request collides with an existing dedicated bearer context and + * the message is received with a TEID not set to zero in the header. + */ + sgw_ue = sgw_ue_find_by_imsi(req->imsi.data, req->imsi.len); + if (!sgw_ue) { + sgw_ue = sgw_ue_add(req->imsi.data, req->imsi.len); + ogs_assert(sgw_ue); + } return sgw_ue; } @@ -463,6 +491,8 @@ sgw_ue_t *sgw_ue_add(uint8_t *imsi, int imsi_len) ogs_list_init(&sgw_ue->sess_list); + ogs_hash_set(self.imsi_ue_hash, sgw_ue->imsi, sgw_ue->imsi_len, sgw_ue); + ogs_list_add(&self.sgw_ue_list, sgw_ue); return sgw_ue; @@ -474,6 +504,8 @@ int sgw_ue_remove(sgw_ue_t *sgw_ue) ogs_list_remove(&self.sgw_ue_list, sgw_ue); + ogs_hash_set(self.imsi_ue_hash, sgw_ue->imsi, sgw_ue->imsi_len, NULL); + sgw_sess_remove_all(sgw_ue); ogs_pool_free(&sgw_ue_pool, sgw_ue); @@ -489,6 +521,25 @@ void sgw_ue_remove_all(void) sgw_ue_remove(sgw_ue); } +sgw_ue_t *sgw_ue_find_by_imsi_bcd(char *imsi_bcd) +{ + uint8_t imsi[OGS_MAX_IMSI_LEN]; + int imsi_len = 0; + + ogs_assert(imsi_bcd); + + ogs_bcd_to_buffer(imsi_bcd, imsi, &imsi_len); + + return sgw_ue_find_by_imsi(imsi, imsi_len); +} + +sgw_ue_t *sgw_ue_find_by_imsi(uint8_t *imsi, int imsi_len) +{ + ogs_assert(imsi && imsi_len); + + return (sgw_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); +} + sgw_ue_t *sgw_ue_find_by_teid(uint32_t teid) { return ogs_pool_find(&sgw_ue_pool, teid); diff --git a/src/sgw/sgw-context.h b/src/sgw/sgw-context.h index 70a501af8e..3ebddb5dec 100644 --- a/src/sgw/sgw-context.h +++ b/src/sgw/sgw-context.h @@ -61,6 +61,8 @@ typedef struct sgw_context_s { ogs_list_t enb_s1u_list; /* eNB GTPU Node List */ ogs_list_t pgw_s5u_list; /* PGW GTPU Node List */ + ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : SGW_UE) */ + ogs_list_t sgw_ue_list; /* SGW_UE List */ } sgw_context_t; @@ -153,6 +155,8 @@ int sgw_context_parse_config(void); ogs_gtp_node_t *sgw_mme_add_by_message(ogs_gtp_message_t *message); sgw_ue_t *sgw_ue_add_by_message(ogs_gtp_message_t *message); +sgw_ue_t *sgw_ue_find_by_imsi(uint8_t *imsi, int imsi_len); +sgw_ue_t *sgw_ue_find_by_imsi_bcd(char *imsi_bcd); sgw_ue_t *sgw_ue_find_by_teid(uint32_t teid); sgw_ue_t *sgw_ue_add(uint8_t *imsi, int imsi_len);