forked from acouzens/open5gs
mme: Introduce initial Gn iface (GTPv1C) support
This interface allows supporting several inter-RAT mobility features towards pre-rel8-SGSNs (SGSNs without S3/S4 GTPV2C interface). Related specs: - 3GPP TS 23.401: -- "5.6 Network Assisted Cell Change" -- "5.15 RAN Information Management (RIM) procedures" -- "Annex D" - 3GPP TS 23.060 (general GERAN<->GERAN mobility) - 3GPP TS 29.060
This commit is contained in:
parent
9ba20f6e22
commit
84ed735204
|
@ -474,6 +474,55 @@ smf:
|
|||
- 127.0.0.4
|
||||
- ::1
|
||||
|
||||
#
|
||||
# <GTP-C Client>
|
||||
#
|
||||
# o Specify SGSN addresses the GTP-C must connect to
|
||||
#
|
||||
# o One SGSN is defined.
|
||||
# If prefer_ipv4 is not true, [fd69:f21d:873c:fa::2] is selected.
|
||||
# sgsn:
|
||||
# - gtpc:
|
||||
# addr:
|
||||
# - 127.0.0.3
|
||||
# - fd69:f21d:873c:fa::2
|
||||
# routes:
|
||||
# - rai:
|
||||
# lai:
|
||||
# plmn_id:
|
||||
# mcc: 001
|
||||
# mnc: 01
|
||||
# lac: 43690
|
||||
# rac: 187
|
||||
# ci: 1223
|
||||
#
|
||||
# o Two SGSNs are defined. Last one is used by default if no
|
||||
# matching RAI+CI route is found.
|
||||
# sgsn:
|
||||
# - gtpc:
|
||||
# addr:
|
||||
# - 127.0.0.3
|
||||
# - fd69:f21d:873c:fa::2
|
||||
# routes:
|
||||
# - rai:
|
||||
# lai:
|
||||
# plmn_id:
|
||||
# mcc: 001
|
||||
# mnc: 01
|
||||
# lac: 43690
|
||||
# rac: 187
|
||||
# ci: 1223
|
||||
# - name: sgsn3.open5gs.org
|
||||
# routes:
|
||||
# - default: true
|
||||
#
|
||||
sgsn:
|
||||
- gtpc:
|
||||
addr:
|
||||
- 127.0.0.3
|
||||
routes:
|
||||
- default: true
|
||||
|
||||
#
|
||||
# o Disable use of IPv4 addresses (only IPv6)
|
||||
# parameter:
|
||||
|
|
|
@ -56,6 +56,14 @@ typedef struct ogs_nas_location_area_identification_s {
|
|||
|
||||
typedef ogs_nas_location_area_identification_t ogs_nas_lai_t;
|
||||
|
||||
/* 3GPP TS 24.008 10.5.5.15 Routing area identification */
|
||||
typedef struct ogs_nas_routing_area_identification_s {
|
||||
ogs_nas_location_area_identification_t lai;
|
||||
uint8_t rac;
|
||||
} __attribute__ ((packed)) ogs_nas_routing_area_identification_t;
|
||||
|
||||
typedef ogs_nas_routing_area_identification_t ogs_nas_rai_t;
|
||||
|
||||
/* 9.9.2.3 Mobile identity
|
||||
* See subclause 10.5.1.4 in 3GPP TS 24.008 [13].
|
||||
* O TLV 7-10 */
|
||||
|
|
|
@ -34,6 +34,8 @@ libmme_sources = files('''
|
|||
emm-build.h
|
||||
esm-handler.h
|
||||
esm-build.h
|
||||
mme-gn-build.h
|
||||
mme-gn-handler.h
|
||||
mme-gtp-path.h
|
||||
mme-s11-build.h
|
||||
mme-s11-handler.h
|
||||
|
@ -68,6 +70,8 @@ libmme_sources = files('''
|
|||
esm-sm.c
|
||||
esm-handler.c
|
||||
esm-build.c
|
||||
mme-gn-build.c
|
||||
mme-gn-handler.c
|
||||
mme-gtp-path.c
|
||||
mme-s11-build.c
|
||||
mme-s11-handler.c
|
||||
|
|
|
@ -37,6 +37,8 @@ int __mme_log_domain;
|
|||
int __emm_log_domain;
|
||||
int __esm_log_domain;
|
||||
|
||||
static OGS_POOL(mme_sgsn_route_pool, mme_sgsn_route_t);
|
||||
static OGS_POOL(mme_sgsn_pool, mme_sgsn_t);
|
||||
static OGS_POOL(mme_sgw_pool, mme_sgw_t);
|
||||
static OGS_POOL(mme_pgw_pool, mme_pgw_t);
|
||||
static OGS_POOL(mme_vlr_pool, mme_vlr_t);
|
||||
|
@ -88,12 +90,15 @@ void mme_context_init(void)
|
|||
ogs_list_init(&self.s1ap_list);
|
||||
ogs_list_init(&self.s1ap_list6);
|
||||
|
||||
ogs_list_init(&self.sgsn_list);
|
||||
ogs_list_init(&self.sgw_list);
|
||||
ogs_list_init(&self.pgw_list);
|
||||
ogs_list_init(&self.enb_list);
|
||||
ogs_list_init(&self.vlr_list);
|
||||
ogs_list_init(&self.csmap_list);
|
||||
|
||||
ogs_pool_init(&mme_sgsn_route_pool, ogs_app()->pool.nf);
|
||||
ogs_pool_init(&mme_sgsn_pool, ogs_app()->pool.nf);
|
||||
ogs_pool_init(&mme_sgw_pool, ogs_app()->pool.nf);
|
||||
ogs_pool_init(&mme_pgw_pool, ogs_app()->pool.nf);
|
||||
ogs_pool_init(&mme_vlr_pool, ogs_app()->pool.nf);
|
||||
|
@ -163,6 +168,8 @@ void mme_context_final(void)
|
|||
|
||||
ogs_pool_final(&mme_enb_pool);
|
||||
|
||||
ogs_pool_final(&mme_sgsn_pool);
|
||||
ogs_pool_final(&mme_sgsn_route_pool);
|
||||
ogs_pool_final(&mme_sgw_pool);
|
||||
ogs_pool_final(&mme_pgw_pool);
|
||||
ogs_pool_final(&mme_csmap_pool);
|
||||
|
@ -282,6 +289,94 @@ static int mme_context_validation(void)
|
|||
return OGS_OK;
|
||||
}
|
||||
|
||||
static int parse_plmn_id(ogs_yaml_iter_t *parent_iter, ogs_plmn_id_t *plmn_id)
|
||||
{
|
||||
const char *mcc = NULL;
|
||||
const char *mnc = NULL;
|
||||
ogs_yaml_iter_t plmn_id_iter;
|
||||
ogs_yaml_iter_recurse(parent_iter,
|
||||
&plmn_id_iter);
|
||||
|
||||
while (ogs_yaml_iter_next(&plmn_id_iter)) {
|
||||
const char *plmn_id_key = ogs_yaml_iter_key(&plmn_id_iter);
|
||||
ogs_assert(plmn_id_key);
|
||||
|
||||
if (!strcmp(plmn_id_key, "mcc")) {
|
||||
mcc = ogs_yaml_iter_value(&plmn_id_iter);
|
||||
} else if (!strcmp(plmn_id_key, "mnc")) {
|
||||
mnc = ogs_yaml_iter_value(&plmn_id_iter);
|
||||
} else
|
||||
ogs_warn("unknown key `%s`", plmn_id_key);
|
||||
}
|
||||
|
||||
if (!mcc || ! mnc)
|
||||
return OGS_ERROR;
|
||||
|
||||
ogs_plmn_id_build(plmn_id, atoi(mcc), atoi(mnc), strlen(mnc));
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
static int parse_lai(ogs_yaml_iter_t *parent_iter, ogs_nas_lai_t *lai)
|
||||
{
|
||||
bool plmn_id_parsed = false;
|
||||
const char *lac = NULL;
|
||||
ogs_plmn_id_t plmn_id;
|
||||
ogs_yaml_iter_t lai_iter;
|
||||
ogs_yaml_iter_recurse(parent_iter, &lai_iter);
|
||||
int rc;
|
||||
|
||||
while (ogs_yaml_iter_next(&lai_iter)) {
|
||||
const char *lai_key = ogs_yaml_iter_key(&lai_iter);
|
||||
ogs_assert(lai_key);
|
||||
|
||||
if (!strcmp(lai_key, "plmn_id")) {
|
||||
rc = parse_plmn_id(&lai_iter, &plmn_id);
|
||||
ogs_assert(rc == OGS_OK);
|
||||
plmn_id_parsed = true;
|
||||
} else if (!strcmp(lai_key, "lac")) {
|
||||
lac = ogs_yaml_iter_value(
|
||||
&lai_iter);
|
||||
} else
|
||||
ogs_warn("unknown key `%s`",
|
||||
lai_key);
|
||||
}
|
||||
|
||||
if (!plmn_id_parsed || !lac)
|
||||
return OGS_ERROR;
|
||||
|
||||
ogs_nas_from_plmn_id( &lai->nas_plmn_id, &plmn_id);
|
||||
lai->lac = atoi(lac);
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
static int parse_rai(ogs_yaml_iter_t *parent_iter, ogs_nas_rai_t *rai)
|
||||
{
|
||||
bool lai_parsed = false, rac_parsed = false;
|
||||
int rc;
|
||||
ogs_yaml_iter_t rai_iter;
|
||||
ogs_yaml_iter_recurse(parent_iter, &rai_iter);
|
||||
|
||||
while (ogs_yaml_iter_next(&rai_iter)) {
|
||||
const char *rai_key = ogs_yaml_iter_key(&rai_iter);
|
||||
ogs_assert(rai_key);
|
||||
|
||||
if (!strcmp(rai_key, "lai")) {
|
||||
rc = parse_lai(&rai_iter, &rai->lai);
|
||||
ogs_assert(rc == OGS_OK);
|
||||
lai_parsed = true;
|
||||
} else if (!strcmp(rai_key, "rac")) {
|
||||
const char *v = ogs_yaml_iter_value(&rai_iter);
|
||||
rai->rac = atoi(v);
|
||||
rac_parsed = true;
|
||||
} else
|
||||
ogs_warn("unknown key `%s`", rai_key);
|
||||
}
|
||||
|
||||
if (!lai_parsed || !rac_parsed)
|
||||
return OGS_ERROR;
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
int mme_context_parse_config(void)
|
||||
{
|
||||
int rv;
|
||||
|
@ -1452,6 +1547,175 @@ int mme_context_parse_config(void)
|
|||
} else
|
||||
ogs_warn("unknown key `%s`", mme_key);
|
||||
}
|
||||
} else if (!strcmp(root_key, "sgsn")) {
|
||||
ogs_yaml_iter_t sgsn_array, sgsn_iter;
|
||||
ogs_yaml_iter_recurse(&root_iter, &sgsn_array);
|
||||
ogs_assert(ogs_yaml_iter_type(&sgsn_array) == YAML_SEQUENCE_NODE);
|
||||
do {
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
if (!ogs_yaml_iter_next(&sgsn_array))
|
||||
break;
|
||||
ogs_yaml_iter_recurse(&sgsn_array, &sgsn_iter);
|
||||
while (ogs_yaml_iter_next(&sgsn_iter)) {
|
||||
const char *sgsn_key = ogs_yaml_iter_key(&sgsn_iter);
|
||||
ogs_assert(sgsn_key);
|
||||
if (!strcmp(sgsn_key, "gtpc")) {
|
||||
ogs_yaml_iter_t gtpc_array, gtpc_iter;
|
||||
ogs_yaml_iter_recurse(&sgsn_iter, >pc_array);
|
||||
do {
|
||||
ogs_sockaddr_t *addr = NULL;
|
||||
int family = AF_UNSPEC;
|
||||
int i, num = 0;
|
||||
const char *hostname[OGS_MAX_NUM_OF_HOSTNAME];
|
||||
uint16_t port = ogs_gtp_self()->gtpc_port;
|
||||
|
||||
if (ogs_yaml_iter_type(>pc_array) ==
|
||||
YAML_MAPPING_NODE) {
|
||||
memcpy(>pc_iter, >pc_array,
|
||||
sizeof(ogs_yaml_iter_t));
|
||||
} else if (ogs_yaml_iter_type(>pc_array) ==
|
||||
YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(>pc_array))
|
||||
break;
|
||||
ogs_yaml_iter_recurse(>pc_array, >pc_iter);
|
||||
} else if (ogs_yaml_iter_type(>pc_array) ==
|
||||
YAML_SCALAR_NODE) {
|
||||
break;
|
||||
} else
|
||||
ogs_assert_if_reached();
|
||||
|
||||
while (ogs_yaml_iter_next(>pc_iter)) {
|
||||
const char *gtpc_key =
|
||||
ogs_yaml_iter_key(>pc_iter);
|
||||
ogs_assert(gtpc_key);
|
||||
if (!strcmp(gtpc_key, "family")) {
|
||||
const char *v = ogs_yaml_iter_value(>pc_iter);
|
||||
if (v) family = atoi(v);
|
||||
if (family != AF_UNSPEC &&
|
||||
family != AF_INET && family != AF_INET6) {
|
||||
ogs_warn("Ignore family(%d) : "
|
||||
"AF_UNSPEC(%d), "
|
||||
"AF_INET(%d), AF_INET6(%d) ",
|
||||
family, AF_UNSPEC, AF_INET, AF_INET6);
|
||||
family = AF_UNSPEC;
|
||||
}
|
||||
} else if (!strcmp(gtpc_key, "addr") ||
|
||||
!strcmp(gtpc_key, "name")) {
|
||||
ogs_yaml_iter_t hostname_iter;
|
||||
ogs_yaml_iter_recurse(>pc_iter,
|
||||
&hostname_iter);
|
||||
ogs_assert(ogs_yaml_iter_type(&hostname_iter) !=
|
||||
YAML_MAPPING_NODE);
|
||||
|
||||
do {
|
||||
if (ogs_yaml_iter_type(&hostname_iter) ==
|
||||
YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(&hostname_iter))
|
||||
break;
|
||||
}
|
||||
|
||||
ogs_assert(num < OGS_MAX_NUM_OF_HOSTNAME);
|
||||
hostname[num++] =
|
||||
ogs_yaml_iter_value(&hostname_iter);
|
||||
} while (
|
||||
ogs_yaml_iter_type(&hostname_iter) ==
|
||||
YAML_SEQUENCE_NODE);
|
||||
} else if (!strcmp(gtpc_key, "port")) {
|
||||
const char *v = ogs_yaml_iter_value(>pc_iter);
|
||||
if (v) port = atoi(v);
|
||||
}
|
||||
}
|
||||
|
||||
addr = NULL;
|
||||
for (i = 0; i < num; i++) {
|
||||
rv = ogs_addaddrinfo(&addr,
|
||||
family, hostname[i], port, 0);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
}
|
||||
|
||||
ogs_filter_ip_version(&addr,
|
||||
ogs_app()->parameter.no_ipv4,
|
||||
ogs_app()->parameter.no_ipv6,
|
||||
ogs_app()->parameter.prefer_ipv4);
|
||||
|
||||
if (addr == NULL) continue;
|
||||
|
||||
sgsn = mme_sgsn_add(addr);
|
||||
ogs_assert(sgsn);
|
||||
|
||||
} while (ogs_yaml_iter_type(>pc_array) == YAML_SEQUENCE_NODE);
|
||||
} else if (!strcmp(sgsn_key, "routes")) {
|
||||
ogs_yaml_iter_t routes_array, routes_iter;
|
||||
ogs_yaml_iter_recurse(&sgsn_iter, &routes_array);
|
||||
do {
|
||||
ogs_nas_rai_t rai;
|
||||
uint16_t cell_id;
|
||||
bool rai_parsed = false, cell_id_parsed = false;
|
||||
bool default_route = false;
|
||||
mme_sgsn_route_t *sgsn_rt = NULL;
|
||||
|
||||
if (ogs_yaml_iter_type(&routes_array) == YAML_MAPPING_NODE) {
|
||||
memcpy(&routes_iter, &routes_array, sizeof(ogs_yaml_iter_t));
|
||||
} else if (ogs_yaml_iter_type(&routes_array) == YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(&routes_array))
|
||||
break;
|
||||
ogs_yaml_iter_recurse(&routes_array, &routes_iter);
|
||||
} else if (ogs_yaml_iter_type(&routes_array) == YAML_SCALAR_NODE) {
|
||||
break;
|
||||
} else
|
||||
ogs_assert_if_reached();
|
||||
|
||||
while (ogs_yaml_iter_next(&routes_iter)) {
|
||||
const char *routes_key = ogs_yaml_iter_key(&routes_iter);
|
||||
ogs_assert(routes_key);
|
||||
if (!strcmp(routes_key, "rai")) {
|
||||
memset(&rai, 0, sizeof(rai));
|
||||
rv = parse_rai(&routes_iter, &rai);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
rai_parsed = true;
|
||||
} else if (!strcmp(routes_key, "ci")) {
|
||||
ogs_yaml_iter_t cell_id_iter;
|
||||
ogs_yaml_iter_recurse(&routes_iter,
|
||||
&cell_id_iter);
|
||||
ogs_assert(ogs_yaml_iter_type(&cell_id_iter)
|
||||
!= YAML_MAPPING_NODE);
|
||||
|
||||
do {
|
||||
const char *v = NULL;
|
||||
|
||||
if (ogs_yaml_iter_type(&cell_id_iter) ==
|
||||
YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(
|
||||
&cell_id_iter))
|
||||
break;
|
||||
}
|
||||
v = ogs_yaml_iter_value(&cell_id_iter);
|
||||
if (v) {
|
||||
cell_id = atoi((char*)v);
|
||||
cell_id_parsed = true;
|
||||
}
|
||||
} while (
|
||||
ogs_yaml_iter_type(&cell_id_iter) ==
|
||||
YAML_SEQUENCE_NODE);
|
||||
} else if (!strcmp(routes_key, "default")) {
|
||||
default_route = true;
|
||||
} else
|
||||
ogs_warn("unknown key `%s`", routes_key);
|
||||
}
|
||||
|
||||
ogs_assert(rai_parsed && cell_id_parsed);
|
||||
ogs_pool_alloc(&mme_sgsn_route_pool, &sgsn_rt);
|
||||
ogs_assert(sgsn_rt);
|
||||
memcpy(&sgsn_rt->rai, &rai, sizeof(rai));
|
||||
sgsn_rt->cell_id = cell_id;
|
||||
ogs_list_add(&sgsn->route_list, sgsn_rt);
|
||||
if (default_route)
|
||||
sgsn->default_route = true;
|
||||
|
||||
} while (ogs_yaml_iter_type(&routes_array) == YAML_SEQUENCE_NODE);
|
||||
}
|
||||
}
|
||||
} while (ogs_yaml_iter_type(&sgsn_array) == YAML_SEQUENCE_NODE);
|
||||
} else if (!strcmp(root_key, "sgw") || !strcmp(root_key, "sgwc")) {
|
||||
ogs_yaml_iter_t sgw_iter;
|
||||
ogs_yaml_iter_recurse(&root_iter, &sgw_iter);
|
||||
|
@ -1881,6 +2145,99 @@ int mme_context_parse_config(void)
|
|||
return OGS_OK;
|
||||
}
|
||||
|
||||
mme_sgsn_t *mme_sgsn_add(ogs_sockaddr_t *addr)
|
||||
{
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
|
||||
ogs_assert(addr);
|
||||
|
||||
ogs_pool_alloc(&mme_sgsn_pool, &sgsn);
|
||||
ogs_assert(sgsn);
|
||||
memset(sgsn, 0, sizeof *sgsn);
|
||||
|
||||
sgsn->gnode.sa_list = addr;
|
||||
|
||||
ogs_list_init(&sgsn->gnode.local_list);
|
||||
ogs_list_init(&sgsn->gnode.remote_list);
|
||||
|
||||
ogs_list_init(&sgsn->route_list);
|
||||
//ogs_list_init(&sgsn->sgsn_ue_list);
|
||||
|
||||
ogs_list_add(&self.sgsn_list, sgsn);
|
||||
|
||||
return sgsn;
|
||||
}
|
||||
|
||||
void mme_sgsn_remove(mme_sgsn_t *sgsn)
|
||||
{
|
||||
mme_sgsn_route_t *rt = NULL, *next_rt = NULL;
|
||||
ogs_assert(sgsn);
|
||||
|
||||
ogs_list_remove(&self.sgsn_list, sgsn);
|
||||
|
||||
ogs_gtp_xact_delete_all(&sgsn->gnode);
|
||||
ogs_freeaddrinfo(sgsn->gnode.sa_list);
|
||||
|
||||
/* Free routes in list */
|
||||
ogs_list_for_each_safe(&sgsn->route_list, next_rt, rt) {
|
||||
ogs_list_remove(&sgsn->route_list, rt);
|
||||
ogs_pool_free(&mme_sgsn_route_pool, rt);
|
||||
}
|
||||
|
||||
ogs_pool_free(&mme_sgsn_pool, sgsn);
|
||||
}
|
||||
|
||||
void mme_sgsn_remove_all(void)
|
||||
{
|
||||
mme_sgsn_t *sgsn = NULL, *next_sgsn = NULL;
|
||||
|
||||
ogs_list_for_each_safe(&self.sgsn_list, next_sgsn, sgsn)
|
||||
mme_sgsn_remove(sgsn);
|
||||
}
|
||||
|
||||
mme_sgsn_t *mme_sgsn_find_by_addr(ogs_sockaddr_t *addr)
|
||||
{
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
|
||||
ogs_assert(addr);
|
||||
|
||||
ogs_list_for_each(&self.sgsn_list, sgsn) {
|
||||
if (ogs_sockaddr_is_equal(&sgsn->gnode.addr, addr) == true)
|
||||
break;
|
||||
}
|
||||
|
||||
return sgsn;
|
||||
}
|
||||
|
||||
/* Find SGSN holding route provided in params. Return SGSN configured as default
|
||||
* routing if no matching route found. */
|
||||
mme_sgsn_t *mme_sgsn_find_by_routing_address(const ogs_nas_rai_t *rai, uint16_t cell_id)
|
||||
{
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
ogs_list_for_each(&self.sgsn_list, sgsn) {
|
||||
mme_sgsn_route_t *rt = NULL;
|
||||
ogs_list_for_each(&sgsn->route_list, rt) {
|
||||
if (rt->cell_id == cell_id &&
|
||||
memcmp(&rt->rai, rai, sizeof(ogs_nas_rai_t)) == 0)
|
||||
return sgsn;
|
||||
}
|
||||
}
|
||||
|
||||
/* No route found, return default route if available: */
|
||||
return mme_sgsn_find_by_default_routing_address();
|
||||
}
|
||||
|
||||
/* Return SGSN configured as default routing */
|
||||
mme_sgsn_t *mme_sgsn_find_by_default_routing_address(void)
|
||||
{
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
ogs_list_for_each(&self.sgsn_list, sgsn) {
|
||||
if (sgsn->default_route)
|
||||
return sgsn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mme_sgw_t *mme_sgw_add(ogs_sockaddr_t *addr)
|
||||
{
|
||||
mme_sgw_t *sgw = NULL;
|
||||
|
|
|
@ -49,6 +49,7 @@ extern int __esm_log_domain;
|
|||
#undef OGS_LOG_DOMAIN
|
||||
#define OGS_LOG_DOMAIN __mme_log_domain
|
||||
|
||||
typedef struct mme_sgsn_s mme_sgsn_t;
|
||||
typedef struct mme_sgw_s mme_sgw_t;
|
||||
typedef struct mme_pgw_s mme_pgw_t;
|
||||
typedef struct mme_vlr_s mme_vlr_t;
|
||||
|
@ -85,9 +86,11 @@ typedef struct mme_context_s {
|
|||
ogs_list_t s1ap_list; /* MME S1AP IPv4 Server List */
|
||||
ogs_list_t s1ap_list6; /* MME S1AP IPv6 Server List */
|
||||
|
||||
ogs_list_t sgw_list; /* SGW GTPC Client List */
|
||||
ogs_list_t sgw_list; /* SGW GTPv2C Client List */
|
||||
mme_sgw_t *sgw; /* Iterator for SGW round-robin */
|
||||
|
||||
ogs_list_t sgsn_list; /* SGW GTPv1C Client List */
|
||||
|
||||
ogs_list_t pgw_list; /* PGW GTPC Client List */
|
||||
ogs_sockaddr_t *pgw_addr; /* First IPv4 Address Selected */
|
||||
ogs_sockaddr_t *pgw_addr6; /* First IPv6 Address Selected */
|
||||
|
@ -161,6 +164,18 @@ typedef struct mme_context_s {
|
|||
} time;
|
||||
} mme_context_t;
|
||||
|
||||
typedef struct mme_sgsn_route_s {
|
||||
ogs_list_t list; /* listed in mme_sgsn_t->route_list */
|
||||
ogs_nas_rai_t rai;
|
||||
uint16_t cell_id;
|
||||
} mme_sgsn_route_t;
|
||||
|
||||
typedef struct mme_sgsn_s {
|
||||
ogs_gtp_node_t gnode;
|
||||
ogs_list_t route_list; /* list of mme_sgsn_route_t */
|
||||
bool default_route; /* use this SGSN as default route */
|
||||
} mme_sgsn_t;
|
||||
|
||||
typedef struct mme_sgw_s {
|
||||
ogs_gtp_node_t gnode;
|
||||
|
||||
|
@ -776,6 +791,13 @@ mme_context_t *mme_self(void);
|
|||
|
||||
int mme_context_parse_config(void);
|
||||
|
||||
mme_sgsn_t *mme_sgsn_add(ogs_sockaddr_t *addr);
|
||||
void mme_sgsn_remove(mme_sgsn_t *sgsn);
|
||||
void mme_sgsn_remove_all(void);
|
||||
mme_sgsn_t *mme_sgsn_find_by_addr(ogs_sockaddr_t *addr);
|
||||
mme_sgsn_t *mme_sgsn_find_by_routing_address(const ogs_nas_rai_t *rai, uint16_t cell_id);
|
||||
mme_sgsn_t *mme_sgsn_find_by_default_routing_address(void);
|
||||
|
||||
mme_sgw_t *mme_sgw_add(ogs_sockaddr_t *addr);
|
||||
void mme_sgw_remove(mme_sgw_t *sgw);
|
||||
void mme_sgw_remove_all(void);
|
||||
|
|
|
@ -97,6 +97,8 @@ const char *mme_event_get_name(mme_event_t *e)
|
|||
case MME_EVENT_SGSAP_LO_CONNREFUSED:
|
||||
return "MME_EVENT_SGSAP_LO_CONNREFUSED";
|
||||
|
||||
case MME_EVENT_GN_MESSAGE:
|
||||
return "MME_EVENT_GN_MESSAGE";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ typedef enum {
|
|||
MME_EVENT_SGSAP_LO_SCTP_COMM_UP,
|
||||
MME_EVENT_SGSAP_LO_CONNREFUSED,
|
||||
|
||||
MME_EVENT_GN_MESSAGE,
|
||||
|
||||
MAX_NUM_OF_MME_EVENT,
|
||||
|
||||
} mme_event_e;
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mme-context.h"
|
||||
|
||||
#include "mme-gn-build.h"
|
||||
|
||||
/* 3GPP TS 29.060 7.5.14.1 RAN Information Relay */
|
||||
ogs_pkbuf_t *mme_gn_build_ran_information_relay(
|
||||
uint8_t type, const uint8_t *buf, size_t len,
|
||||
const ogs_nas_rai_t *rai, uint16_t cell_id)
|
||||
{
|
||||
ogs_gtp1_message_t gtp1_message;
|
||||
ogs_gtp1_ran_information_relay_t *req = NULL;
|
||||
uint8_t rt_addr[sizeof(*rai) + sizeof(cell_id)];
|
||||
|
||||
ogs_debug("[Gn] build RAN Information Relay");
|
||||
|
||||
|
||||
req = >p1_message.ran_information_relay;
|
||||
memset(>p1_message, 0, sizeof(ogs_gtp1_message_t));
|
||||
|
||||
/* 3GPP TS 29.060 7.7.43 RAN Transparent Container, Mandatory */
|
||||
req->ran_transparent_container.presence = 1;
|
||||
req->ran_transparent_container.data = (void*)buf;
|
||||
req->ran_transparent_container.len = len;
|
||||
|
||||
/* 3GPP TS 29.060 7.7.57 RAN RIM Routing Address, Optional */
|
||||
req->rim_routing_address.presence = !!rai;
|
||||
if (req->rim_routing_address.presence) {
|
||||
memcpy(&rt_addr[0], rai, sizeof(*rai));
|
||||
memcpy(&rt_addr[sizeof(*rai)], &cell_id, sizeof(cell_id));
|
||||
req->rim_routing_address.data = &rt_addr[0];
|
||||
req->rim_routing_address.len = sizeof(rt_addr);
|
||||
}
|
||||
|
||||
/* 3GPP TS 29.060 7.7.77 RIM Routing Address Discriminator, Optional */
|
||||
/* 3GPP TS 48.018 11.3.70 RIM Routing Information IE: */
|
||||
uint8_t rim_routing_address_disc = 0; /* "A Cell Identifier is used to identify a GERAN cell."" */
|
||||
req->rim_routing_address_discriminator.presence = req->rim_routing_address.presence;
|
||||
req->rim_routing_address_discriminator.data = &rim_routing_address_disc;
|
||||
req->rim_routing_address_discriminator.len = 1;
|
||||
|
||||
gtp1_message.h.type = type;
|
||||
return ogs_gtp1_build_msg(>p1_message);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MME_GN_BUILD_H
|
||||
#define MME_GN_BUILD_H
|
||||
|
||||
#include "ogs-nas-eps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
ogs_pkbuf_t *mme_gn_build_ran_information_relay(
|
||||
uint8_t type, const uint8_t *buf, size_t len,
|
||||
const ogs_nas_rai_t *rai, uint16_t cell_id);
|
||||
|
||||
#endif /* MME_S11_BUILD_H */
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mme-event.h"
|
||||
#include "mme-sm.h"
|
||||
#include "mme-context.h"
|
||||
#include "mme-timer.h"
|
||||
|
||||
#include "mme-gtp-path.h"
|
||||
#include "mme-fd-path.h"
|
||||
#include "mme-path.h"
|
||||
|
||||
#include "mme-gn-build.h"
|
||||
#include "mme-gn-handler.h"
|
||||
|
||||
void mme_gn_handle_echo_request(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_echo_request_t *req)
|
||||
{
|
||||
ogs_assert(xact);
|
||||
ogs_assert(req);
|
||||
|
||||
ogs_debug("[Gn] Receiving Echo Request");
|
||||
/* FIXME : Implementing recovery counter correctly */
|
||||
ogs_gtp1_send_echo_response(xact, 0);
|
||||
}
|
||||
|
||||
void mme_gn_handle_echo_response(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_echo_response_t *req)
|
||||
{
|
||||
/* Not Implemented */
|
||||
}
|
||||
|
||||
|
||||
void mme_gn_handle_ran_information_relay(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req)
|
||||
{
|
||||
ogs_debug("[Gn] Receiving RAN Information Relay");
|
||||
/* Not Implemented */
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MME_GN_HANDLER_H
|
||||
#define MME_GN_HANDLER_H
|
||||
|
||||
#include "mme-context.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void mme_gn_handle_echo_request(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_echo_request_t *req);
|
||||
|
||||
void mme_gn_handle_echo_response(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_echo_response_t *req);
|
||||
|
||||
void mme_gn_handle_ran_information_relay(
|
||||
ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MME_S11_HANDLER_H */
|
|
@ -20,13 +20,14 @@
|
|||
#include "ogs-gtp.h"
|
||||
|
||||
#include "mme-event.h"
|
||||
#include "mme-gn-build.h"
|
||||
#include "mme-gtp-path.h"
|
||||
#include "mme-path.h"
|
||||
#include "s1ap-path.h"
|
||||
#include "mme-s11-build.h"
|
||||
#include "mme-sm.h"
|
||||
|
||||
static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
|
||||
static void _gtpv1v2_c_recv_cb(short when, ogs_socket_t fd, void *data)
|
||||
{
|
||||
int rv;
|
||||
char buf[OGS_ADDRSTRLEN];
|
||||
|
@ -36,6 +37,8 @@ static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
|
|||
ogs_pkbuf_t *pkbuf = NULL;
|
||||
ogs_sockaddr_t from;
|
||||
mme_sgw_t *sgw = NULL;
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
uint8_t gtp_ver;
|
||||
|
||||
ogs_assert(fd != INVALID_SOCKET);
|
||||
|
||||
|
@ -53,6 +56,21 @@ static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
|
|||
|
||||
ogs_pkbuf_trim(pkbuf, size);
|
||||
|
||||
gtp_ver = ((ogs_gtp2_header_t *)pkbuf->data)->version;
|
||||
switch (gtp_ver) {
|
||||
case 1:
|
||||
sgsn = mme_sgsn_find_by_addr(&from);
|
||||
if (!sgsn) {
|
||||
ogs_error("Unknown SGSN : %s", OGS_ADDR(&from, buf));
|
||||
ogs_pkbuf_free(pkbuf);
|
||||
return;
|
||||
}
|
||||
ogs_assert(sgsn);
|
||||
e = mme_event_new(MME_EVENT_GN_MESSAGE);
|
||||
ogs_assert(e);
|
||||
e->gnode = &sgsn->gnode;
|
||||
break;
|
||||
case 2:
|
||||
sgw = mme_sgw_find_by_addr(&from);
|
||||
if (!sgw) {
|
||||
ogs_error("Unknown SGW : %s", OGS_ADDR(&from, buf));
|
||||
|
@ -60,10 +78,16 @@ static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
|
|||
return;
|
||||
}
|
||||
ogs_assert(sgw);
|
||||
|
||||
e = mme_event_new(MME_EVENT_S11_MESSAGE);
|
||||
ogs_assert(e);
|
||||
e->gnode = (ogs_gtp_node_t *)sgw;
|
||||
e->gnode = &sgw->gnode;
|
||||
break;
|
||||
default:
|
||||
ogs_warn("Rx unexpected GTP version %u", gtp_ver);
|
||||
ogs_pkbuf_free(pkbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
e->pkbuf = pkbuf;
|
||||
|
||||
rv = ogs_queue_push(ogs_app()->queue, e);
|
||||
|
@ -159,13 +183,14 @@ int mme_gtp_open(void)
|
|||
ogs_socknode_t *node = NULL;
|
||||
ogs_sock_t *sock = NULL;
|
||||
mme_sgw_t *sgw = NULL;
|
||||
mme_sgsn_t *sgsn = NULL;
|
||||
|
||||
ogs_list_for_each(&ogs_gtp_self()->gtpc_list, node) {
|
||||
sock = ogs_gtp_server(node);
|
||||
if (!sock) return OGS_ERROR;
|
||||
|
||||
node->poll = ogs_pollset_add(ogs_app()->pollset,
|
||||
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
|
||||
OGS_POLLIN, sock->fd, _gtpv1v2_c_recv_cb, sock);
|
||||
ogs_assert(node->poll);
|
||||
}
|
||||
ogs_list_for_each(&ogs_gtp_self()->gtpc_list6, node) {
|
||||
|
@ -173,7 +198,7 @@ int mme_gtp_open(void)
|
|||
if (!sock) return OGS_ERROR;
|
||||
|
||||
node->poll = ogs_pollset_add(ogs_app()->pollset,
|
||||
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
|
||||
OGS_POLLIN, sock->fd, _gtpv1v2_c_recv_cb, sock);
|
||||
ogs_assert(node->poll);
|
||||
}
|
||||
|
||||
|
@ -188,7 +213,14 @@ int mme_gtp_open(void)
|
|||
ogs_list_for_each(&mme_self()->sgw_list, sgw) {
|
||||
rv = ogs_gtp_connect(
|
||||
ogs_gtp_self()->gtpc_sock, ogs_gtp_self()->gtpc_sock6,
|
||||
(ogs_gtp_node_t *)sgw);
|
||||
&sgw->gnode);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
}
|
||||
|
||||
ogs_list_for_each(&mme_self()->sgsn_list, sgsn) {
|
||||
rv = ogs_gtp_connect(
|
||||
ogs_gtp_self()->gtpc_sock, ogs_gtp_self()->gtpc_sock6,
|
||||
&sgsn->gnode);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
}
|
||||
|
||||
|
@ -725,3 +757,41 @@ int mme_gtp_send_bearer_resource_command(
|
|||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
int mme_gtp1_send_ran_information_relay(
|
||||
mme_sgsn_t *sgsn, const uint8_t *buf, size_t len,
|
||||
const ogs_nas_rai_t *rai, uint16_t cell_id)
|
||||
{
|
||||
int rv;
|
||||
ogs_gtp1_header_t h;
|
||||
ogs_pkbuf_t *pkbuf = NULL;
|
||||
ogs_gtp_xact_t *xact = NULL;
|
||||
|
||||
ogs_assert(sgsn);
|
||||
ogs_assert(buf);
|
||||
|
||||
memset(&h, 0, sizeof(ogs_gtp1_header_t));
|
||||
h.type = OGS_GTP1_RAN_INFORMATION_RELAY_TYPE;
|
||||
h.teid = 0;
|
||||
|
||||
pkbuf = mme_gn_build_ran_information_relay(h.type, buf, len, rai, cell_id);
|
||||
if (!pkbuf) {
|
||||
ogs_error("mme_gn_build_ran_information_relay() failed");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
xact = ogs_gtp1_xact_local_create(&sgsn->gnode, &h, pkbuf, NULL, NULL);
|
||||
if (!xact) {
|
||||
ogs_error("ogs_gtp1_xact_local_create() failed");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
/* TS 29.060 8.2: "The RAN Information Relay message, where the Tunnel
|
||||
* Endpoint Identifier shall be set to all zeroes." */
|
||||
xact->local_teid = 0;
|
||||
|
||||
rv = ogs_gtp_xact_commit(xact);
|
||||
ogs_expect(rv == OGS_OK);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request(
|
|||
int mme_gtp_send_bearer_resource_command(
|
||||
mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message);
|
||||
|
||||
int mme_gtp1_send_ran_information_relay(
|
||||
mme_sgsn_t *sgsn, const uint8_t *buf, size_t len,
|
||||
const ogs_nas_rai_t *rai, uint16_t cell_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "nas-path.h"
|
||||
#include "emm-handler.h"
|
||||
#include "esm-handler.h"
|
||||
#include "mme-gn-handler.h"
|
||||
#include "mme-gtp-path.h"
|
||||
#include "mme-s11-handler.h"
|
||||
#include "mme-fd-path.h"
|
||||
|
@ -79,6 +80,7 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
|
|||
ogs_gtp_node_t *gnode = NULL;
|
||||
ogs_gtp_xact_t *xact = NULL;
|
||||
ogs_gtp2_message_t gtp_message;
|
||||
ogs_gtp1_message_t gtp1_message;
|
||||
|
||||
mme_vlr_t *vlr = NULL;
|
||||
|
||||
|
@ -630,6 +632,41 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
|
|||
}
|
||||
break;
|
||||
|
||||
case MME_EVENT_GN_MESSAGE:
|
||||
pkbuf = e->pkbuf;
|
||||
ogs_assert(pkbuf);
|
||||
|
||||
if (ogs_gtp1_parse_msg(>p1_message, pkbuf) != OGS_OK) {
|
||||
ogs_error("ogs_gtp1_parse_msg() failed");
|
||||
ogs_pkbuf_free(pkbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
gnode = e->gnode;
|
||||
ogs_assert(gnode);
|
||||
|
||||
rv = ogs_gtp1_xact_receive(gnode, >p1_message.h, &xact);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_pkbuf_free(pkbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (gtp1_message.h.type) {
|
||||
case OGS_GTP1_ECHO_REQUEST_TYPE:
|
||||
mme_gn_handle_echo_request(xact, >p1_message.echo_request);
|
||||
break;
|
||||
case OGS_GTP1_ECHO_RESPONSE_TYPE:
|
||||
mme_gn_handle_echo_response(xact, >p1_message.echo_response);
|
||||
break;
|
||||
case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE:
|
||||
mme_gn_handle_ran_information_relay(xact, >p1_message.ran_information_relay);
|
||||
break;
|
||||
default:
|
||||
ogs_warn("Not implemented(type:%d)", gtp1_message.h.type);
|
||||
break;
|
||||
}
|
||||
ogs_pkbuf_free(pkbuf);
|
||||
break;
|
||||
|
||||
case MME_EVENT_SGSAP_LO_SCTP_COMM_UP:
|
||||
sock = e->sock;
|
||||
|
|
Loading…
Reference in New Issue