diff --git a/configs/open5gs/mme.yaml.in b/configs/open5gs/mme.yaml.in index 17e9c7fc8..5c605086c 100644 --- a/configs/open5gs/mme.yaml.in +++ b/configs/open5gs/mme.yaml.in @@ -474,6 +474,55 @@ smf: - 127.0.0.4 - ::1 +# +# +# +# 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: diff --git a/lib/nas/eps/types.h b/lib/nas/eps/types.h index 3d8909cd2..ba15b748b 100644 --- a/lib/nas/eps/types.h +++ b/lib/nas/eps/types.h @@ -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 */ diff --git a/src/mme/meson.build b/src/mme/meson.build index b7fabaf6e..862d30992 100644 --- a/src/mme/meson.build +++ b/src/mme/meson.build @@ -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 diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 4035500ac..5fbda8b94 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.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; diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index e2a24aff7..fb9c0215f 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -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); diff --git a/src/mme/mme-event.c b/src/mme/mme-event.c index 6b0183b73..36c4bd624 100644 --- a/src/mme/mme-event.c +++ b/src/mme/mme-event.c @@ -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; } diff --git a/src/mme/mme-event.h b/src/mme/mme-event.h index 146c5cbb9..97869070f 100644 --- a/src/mme/mme-event.h +++ b/src/mme/mme-event.h @@ -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; diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c new file mode 100644 index 000000000..dc88063dd --- /dev/null +++ b/src/mme/mme-gn-build.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + */ + +#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); +} \ No newline at end of file diff --git a/src/mme/mme-gn-build.h b/src/mme/mme-gn-build.h new file mode 100644 index 000000000..fc66282c6 --- /dev/null +++ b/src/mme/mme-gn-build.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + */ + +#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 */ diff --git a/src/mme/mme-gn-handler.c b/src/mme/mme-gn-handler.c new file mode 100644 index 000000000..ccf8020df --- /dev/null +++ b/src/mme/mme-gn-handler.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + */ + +#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 */ +} diff --git a/src/mme/mme-gn-handler.h b/src/mme/mme-gn-handler.h new file mode 100644 index 000000000..3cf2e661b --- /dev/null +++ b/src/mme/mme-gn-handler.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + */ + +#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 */ diff --git a/src/mme/mme-gtp-path.c b/src/mme/mme-gtp-path.c index 8596a4ffe..cce8a714e 100644 --- a/src/mme/mme-gtp-path.c +++ b/src/mme/mme-gtp-path.c @@ -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; +} diff --git a/src/mme/mme-gtp-path.h b/src/mme/mme-gtp-path.h index 839a11fed..37773e115 100644 --- a/src/mme/mme-gtp-path.h +++ b/src/mme/mme-gtp-path.h @@ -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 diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 4617c448b..87554cba4 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -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;