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:
Pau Espin 2023-07-06 18:03:42 +02:00 committed by Sukchan Lee
parent 9ba20f6e22
commit 84ed735204
14 changed files with 763 additions and 12 deletions

View File

@ -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:

View File

@ -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 */

View File

@ -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

View File

@ -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, &gtpc_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(&gtpc_array) ==
YAML_MAPPING_NODE) {
memcpy(&gtpc_iter, &gtpc_array,
sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&gtpc_array))
break;
ogs_yaml_iter_recurse(&gtpc_array, &gtpc_iter);
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SCALAR_NODE) {
break;
} else
ogs_assert_if_reached();
while (ogs_yaml_iter_next(&gtpc_iter)) {
const char *gtpc_key =
ogs_yaml_iter_key(&gtpc_iter);
ogs_assert(gtpc_key);
if (!strcmp(gtpc_key, "family")) {
const char *v = ogs_yaml_iter_value(&gtpc_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(&gtpc_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(&gtpc_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(&gtpc_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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

62
src/mme/mme-gn-build.c Normal file
View File

@ -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 = &gtp1_message.ran_information_relay;
memset(&gtp1_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(&gtp1_message);
}

37
src/mme/mme-gn-build.h Normal file
View File

@ -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 */

55
src/mme/mme-gn-handler.c Normal file
View File

@ -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 */
}

42
src/mme/mme-gn-handler.h Normal file
View File

@ -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 */

View File

@ -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,17 +56,38 @@ static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
ogs_pkbuf_trim(pkbuf, size);
sgw = mme_sgw_find_by_addr(&from);
if (!sgw) {
ogs_error("Unknown SGW : %s", OGS_ADDR(&from, buf));
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));
ogs_pkbuf_free(pkbuf);
return;
}
ogs_assert(sgw);
e = mme_event_new(MME_EVENT_S11_MESSAGE);
ogs_assert(e);
e->gnode = &sgw->gnode;
break;
default:
ogs_warn("Rx unexpected GTP version %u", gtp_ver);
ogs_pkbuf_free(pkbuf);
return;
}
ogs_assert(sgw);
e = mme_event_new(MME_EVENT_S11_MESSAGE);
ogs_assert(e);
e->gnode = (ogs_gtp_node_t *)sgw;
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;
}

View File

@ -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

View File

@ -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(&gtp1_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, &gtp1_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, &gtp1_message.echo_request);
break;
case OGS_GTP1_ECHO_RESPONSE_TYPE:
mme_gn_handle_echo_response(xact, &gtp1_message.echo_response);
break;
case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE:
mme_gn_handle_ran_information_relay(xact, &gtp1_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;