diff options
Diffstat (limited to 'src')
49 files changed, 2781 insertions, 6413 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c45d3ab46..e389b7f65 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,5 @@ SUBDIRS = \ gprs \ sgsn \ - gbproxy \ gtphub \ $(NULL) diff --git a/src/gbproxy/Makefile.am b/src/gbproxy/Makefile.am deleted file mode 100644 index 6876f68a5..000000000 --- a/src/gbproxy/Makefile.am +++ /dev/null @@ -1,46 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - -fno-strict-aliasing \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMOGB_CFLAGS) \ - $(LIBOSMOGSUPCLIENT_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBGTP_CFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-gbproxy \ - $(NULL) - -osmo_gbproxy_SOURCES = \ - gb_proxy.c \ - gb_proxy_main.c \ - gb_proxy_vty.c \ - gb_proxy_ctrl.c \ - gb_proxy_patch.c \ - gb_proxy_tlli.c \ - gb_proxy_peer.c \ - $(NULL) -osmo_gbproxy_LDADD = \ - $(top_builddir)/src/gprs/gprs_gb_parse.o \ - $(top_builddir)/src/gprs/gprs_llc_parse.o \ - $(top_builddir)/src/gprs/crc24.o \ - $(top_builddir)/src/gprs/gprs_utils.o \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOGB_LIBS) \ - $(LIBGTP_LIBS) \ - -lrt \ - $(NULL) diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c deleted file mode 100644 index 3f471ff82..000000000 --- a/src/gbproxy/gb_proxy.c +++ /dev/null @@ -1,1454 +0,0 @@ -/* NS-over-IP proxy */ - -/* (C) 2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2010-2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> -#include <errno.h> -#include <sys/fcntl.h> -#include <sys/stat.h> -#include <arpa/inet.h> -#include <time.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/stats.h> - -#include <osmocom/gprs/gprs_ns.h> -#include <osmocom/gprs/gprs_bssgp.h> - -#include <osmocom/gsm/gsm_utils.h> - -#include <osmocom/sgsn/signal.h> -#include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_gb_parse.h> -#include <osmocom/sgsn/gb_proxy.h> - -#include <osmocom/sgsn/gprs_llc.h> -#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> -#include <osmocom/sgsn/gprs_utils.h> - -extern void *tall_sgsn_ctx; - -static const struct rate_ctr_desc global_ctr_description[] = { - { "inv-bvci", "Invalid BVC Identifier " }, - { "inv-lai", "Invalid Location Area Identifier" }, - { "inv-rai", "Invalid Routing Area Identifier " }, - { "inv-nsei", "No BVC established for NSEI " }, - { "proto-err:bss", "BSSGP protocol error (BSS )" }, - { "proto-err:sgsn", "BSSGP protocol error (SGSN)" }, - { "not-supp:bss", "Feature not supported (BSS )" }, - { "not-supp:sgsn", "Feature not supported (SGSN)" }, - { "restart:sgsn", "Restarted RESET procedure (SGSN)" }, - { "tx-err:sgsn", "NS Transmission error (SGSN)" }, - { "error", "Other error " }, - { "mod-peer-err", "Patch error: no peer " }, -}; - -static const struct rate_ctr_group_desc global_ctrg_desc = { - .group_name_prefix = "gbproxy:global", - .group_description = "GBProxy Global Statistics", - .num_ctr = ARRAY_SIZE(global_ctr_description), - .ctr_desc = global_ctr_description, - .class_id = OSMO_STATS_CLASS_GLOBAL, -}; - -static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, - uint16_t ns_bvci); -static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, - uint16_t ns_bvci, uint16_t sgsn_nsei); -static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info); - -static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei) -{ - if (peer->nsei != nsei) { - LOGP(DGPRS, LOGL_NOTICE, "Peer entry doesn't match current NSEI " - "BVCI=%u via NSEI=%u (expected NSEI=%u)\n", - peer->bvci, nsei, peer->nsei); - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_INV_NSEI]); - return 0; - } - - return 1; -} - -/* strip off the NS header */ -static void strip_ns_hdr(struct msgb *msg) -{ - int strip_len = msgb_bssgph(msg) - msg->data; - msgb_pull(msg, strip_len); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static void gprs_put_identity_req(struct msgb *msg, uint8_t id_type) -{ - struct gsm48_hdr *gh; - - id_type &= GSM_MI_TYPE_MASK; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ID_REQ; - gh->data[0] = id_type; -} - -/* Transmit Chapter 9.4.6.2 Detach Accept (mobile originated detach) */ -static void gprs_put_mo_detach_acc(struct msgb *msg) -{ - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_DETACH_ACK; - gh->data[0] = 0; /* no force to standby */ -} - -static void gprs_push_llc_ui(struct msgb *msg, - int is_uplink, unsigned sapi, unsigned nu) -{ - const uint8_t e_bit = 0; - const uint8_t pm_bit = 1; - const uint8_t cr_bit = is_uplink ? 0 : 1; - uint8_t *llc; - uint8_t *fcs_field; - uint32_t fcs; - - nu &= 0x01ff; /* 9 Bit */ - - llc = msgb_push(msg, 3); - llc[0] = (cr_bit << 6) | (sapi & 0x0f); - llc[1] = 0xc0 | (nu >> 6); /* UI frame */ - llc[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); - - fcs = gprs_llc_fcs(llc, msgb_length(msg)); - fcs_field = msgb_put(msg, 3); - fcs_field[0] = (uint8_t)(fcs >> 0); - fcs_field[1] = (uint8_t)(fcs >> 8); - fcs_field[2] = (uint8_t)(fcs >> 16); -} - -static void gprs_push_bssgp_dl_unitdata(struct msgb *msg, - uint32_t tlli) -{ - struct bssgp_ud_hdr *budh; - uint8_t *llc = msgb_data(msg); - size_t llc_size = msgb_length(msg); - const size_t llc_ie_hdr_size = 3; - const uint8_t qos_profile[] = {0x00, 0x50, 0x20}; /* hard-coded */ - const uint8_t lifetime[] = {0x02, 0x58}; /* 6s hard-coded */ - - const size_t bssgp_overhead = sizeof(*budh) + - TVLV_GROSS_LEN(sizeof(lifetime)) + llc_ie_hdr_size; - uint8_t *ie; - uint32_t tlli_be = htonl(tlli); - - budh = (struct bssgp_ud_hdr *)msgb_push(msg, bssgp_overhead); - - budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; - memcpy(&budh->tlli, &tlli_be, sizeof(budh->tlli)); - memcpy(&budh->qos_profile, qos_profile, sizeof(budh->qos_profile)); - - ie = budh->data; - tvlv_put(ie, BSSGP_IE_PDU_LIFETIME, sizeof(lifetime), lifetime); - ie += TVLV_GROSS_LEN(sizeof(lifetime)); - - /* Note: Add alignment before the LLC IE if inserting other IE */ - - *(ie++) = BSSGP_IE_LLC_PDU; - *(ie++) = llc_size / 256; - *(ie++) = llc_size % 256; - - OSMO_ASSERT(ie == llc); - - msgb_bssgph(msg) = (uint8_t *)budh; - msgb_tlli(msg) = tlli; -} - -/* update peer according to the BSS message */ -static void gbprox_update_current_raid(uint8_t *raid_enc, - struct gbproxy_peer *peer, - const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - const struct osmo_plmn_id old_plmn = state->local_plmn; - struct gprs_ra_id raid; - - if (!raid_enc) - return; - - gsm48_parse_ra(&raid, raid_enc); - - /* save source side MCC/MNC */ - if (!peer->cfg->core_plmn.mcc || raid.mcc == peer->cfg->core_plmn.mcc) { - state->local_plmn.mcc = 0; - } else { - state->local_plmn.mcc = raid.mcc; - } - - if (!peer->cfg->core_plmn.mnc - || !osmo_mnc_cmp(raid.mnc, raid.mnc_3_digits, - peer->cfg->core_plmn.mnc, peer->cfg->core_plmn.mnc_3_digits)) { - state->local_plmn.mnc = 0; - state->local_plmn.mnc_3_digits = false; - } else { - state->local_plmn.mnc = raid.mnc; - state->local_plmn.mnc_3_digits = raid.mnc_3_digits; - } - - if (osmo_plmn_cmp(&old_plmn, &state->local_plmn)) - LOGP(DGPRS, LOGL_NOTICE, - "Patching RAID %sactivated, msg: %s, " - "local: %s, core: %s\n", - state->local_plmn.mcc || state->local_plmn.mnc ? - "" : "de", - log_text, - osmo_plmn_name(&state->local_plmn), - osmo_plmn_name2(&peer->cfg->core_plmn)); -} - -uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, - uint32_t sgsn_ptmsi) -{ - uint32_t bss_ptmsi; - int max_retries = 23, rc = 0; - if (!peer->cfg->patch_ptmsi) { - bss_ptmsi = sgsn_ptmsi; - } else { - do { - rc = osmo_get_rand_id((uint8_t *) &bss_ptmsi, sizeof(bss_ptmsi)); - if (rc < 0) { - bss_ptmsi = GSM_RESERVED_TMSI; - break; - } - - bss_ptmsi = bss_ptmsi | GSM23003_TMSI_SGSN_MASK; - - if (gbproxy_link_info_by_ptmsi(peer, bss_ptmsi)) - bss_ptmsi = GSM_RESERVED_TMSI; - } while (bss_ptmsi == GSM_RESERVED_TMSI && max_retries--); - } - - if (bss_ptmsi == GSM_RESERVED_TMSI) - LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a BSS P-TMSI: %d (%s)\n", rc, strerror(-rc)); - - return bss_ptmsi; -} - -uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - uint32_t bss_tlli) -{ - uint32_t sgsn_tlli; - int max_retries = 23, rc = 0; - if (!peer->cfg->patch_ptmsi) { - sgsn_tlli = bss_tlli; - } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && - gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) { - sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, - TLLI_FOREIGN); - } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && - gprs_tlli_type(bss_tlli) == TLLI_LOCAL) { - sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, - TLLI_LOCAL); - } else { - do { - /* create random TLLI, 0b01111xxx... */ - rc = osmo_get_rand_id((uint8_t *) &sgsn_tlli, sizeof(sgsn_tlli)); - if (rc < 0) { - sgsn_tlli = 0; - break; - } - - sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; - - if (gbproxy_link_info_by_any_sgsn_tlli(peer, sgsn_tlli)) - sgsn_tlli = 0; - } while (!sgsn_tlli && max_retries--); - } - - if (!sgsn_tlli) - LOGP(DGPRS, LOGL_ERROR, "Failed to allocate an SGSN TLLI: %d (%s)\n", rc, strerror(-rc)); - - return sgsn_tlli; -} - -void gbproxy_reset_link(struct gbproxy_link_info *link_info) -{ - gbproxy_reset_imsi_acquisition(link_info); -} - -/* Returns != 0 iff IMSI acquisition was in progress */ -static int gbproxy_restart_imsi_acquisition(struct gbproxy_link_info* link_info) -{ - int in_progress = 0; - if (!link_info) - return 0; - - if (link_info->imsi_acq_pending) - in_progress = 1; - - gbproxy_link_info_discard_messages(link_info); - link_info->imsi_acq_pending = false; - - return in_progress; -} - -static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info) -{ - gbproxy_restart_imsi_acquisition(link_info); - link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; -} - -/* Got identity response with IMSI, assuming the request had - * been generated by the gbproxy */ -static int gbproxy_flush_stored_messages(struct gbproxy_peer *peer, - time_t now, - struct gbproxy_link_info* link_info) -{ - int rc; - struct msgb *stored_msg; - - /* Patch and flush stored messages towards the SGSN */ - while ((stored_msg = msgb_dequeue_count(&link_info->stored_msgs, - &link_info->stored_msgs_len))) { - struct gprs_gb_parse_context tmp_parse_ctx = {0}; - tmp_parse_ctx.to_bss = 0; - tmp_parse_ctx.peer_nsei = msgb_nsei(stored_msg); - int len_change = 0; - - gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - &tmp_parse_ctx); - gbproxy_patch_bssgp(stored_msg, msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - peer, link_info, &len_change, - &tmp_parse_ctx); - - rc = gbproxy_update_link_state_after(peer, link_info, now, - &tmp_parse_ctx); - if (rc == 1) { - LOGP(DLLC, LOGL_NOTICE, "link_info deleted while flushing stored messages\n"); - msgb_free(stored_msg); - return -1; - } - - rc = gbprox_relay2sgsn(peer->cfg, stored_msg, - msgb_bvci(stored_msg), link_info->sgsn_nsei); - - if (rc < 0) - LOGP(DLLC, LOGL_ERROR, - "NSEI=%d(BSS) failed to send stored message " - "(%s)\n", - tmp_parse_ctx.peer_nsei, - tmp_parse_ctx.llc_msg_name ? - tmp_parse_ctx.llc_msg_name : "BSSGP"); - msgb_free(stored_msg); - } - - return 0; -} - -static int gbproxy_gsm48_to_peer(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci, - struct msgb *msg /* Takes msg ownership */) -{ - int rc; - - /* Workaround to avoid N(U) collisions and to enable a restart - * of the IMSI acquisition procedure. This will work unless the - * SGSN has an initial V(UT) within [256-32, 256+n_retries] - * (see GSM 04.64, 8.4.2). */ - gprs_push_llc_ui(msg, 0, GPRS_SAPI_GMM, link_info->vu_gen_tx_bss); - link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512; - - gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current); - rc = gbprox_relay2peer(msg, peer, bvci); - msgb_free(msg); - return rc; -} - -static void gbproxy_acquire_imsi(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci) -{ - struct msgb *idreq_msg; - - /* Send IDENT REQ */ - idreq_msg = gsm48_msgb_alloc_name("GSM 04.08 ACQ IMSI"); - gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); - gbproxy_gsm48_to_peer(peer, link_info, bvci, idreq_msg); -} - -static void gbproxy_tx_detach_acc(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci) -{ - struct msgb *detacc_msg; - - /* Send DETACH ACC */ - detacc_msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACC"); - gprs_put_mo_detach_acc(detacc_msg); - gbproxy_gsm48_to_peer(peer, link_info, bvci, detacc_msg); -} - -/* Return != 0 iff msg still needs to be processed */ -static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer, - struct msgb *msg, - time_t now, - struct gbproxy_link_info* link_info, - struct gprs_gb_parse_context *parse_ctx) -{ - struct msgb *stored_msg; - - if (!link_info) - return 1; - - if (!link_info->imsi_acq_pending && link_info->imsi_len > 0) - return 1; - - if (parse_ctx->g48_hdr) - switch (parse_ctx->g48_hdr->msg_type) - { - case GSM48_MT_GMM_RA_UPD_REQ: - case GSM48_MT_GMM_ATTACH_REQ: - if (gbproxy_restart_imsi_acquisition(link_info)) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition was in progress " - "when receiving an %s.\n", - msgb_nsei(msg), parse_ctx->llc_msg_name); - } - break; - case GSM48_MT_GMM_DETACH_REQ: - /* Nothing has been sent to the SGSN yet */ - if (link_info->imsi_acq_pending) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition was in progress " - "when receiving a DETACH_REQ.\n", - msgb_nsei(msg)); - } - if (!parse_ctx->invalidate_tlli) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI not yet acquired, " - "faking a DETACH_ACC.\n", - msgb_nsei(msg)); - gbproxy_tx_detach_acc(peer, link_info, msgb_bvci(msg)); - parse_ctx->invalidate_tlli = 1; - } - gbproxy_reset_imsi_acquisition(link_info); - gbproxy_update_link_state_after(peer, link_info, now, - parse_ctx); - return 0; - } - - if (link_info->imsi_acq_pending && link_info->imsi_len > 0) { - int is_ident_resp = - parse_ctx->g48_hdr && - gsm48_hdr_pdisc(parse_ctx->g48_hdr) == GSM48_PDISC_MM_GPRS && - gsm48_hdr_msg_type(parse_ctx->g48_hdr) == GSM48_MT_GMM_ID_RESP; - - LOGP(DLLC, LOGL_DEBUG, - "NSEI=%d(BSS) IMSI acquisition succeeded, " - "flushing stored messages\n", - msgb_nsei(msg)); - /* The IMSI is now available. If flushing the messages fails, - * then link_info has been deleted and we should return - * immediately. */ - if (gbproxy_flush_stored_messages(peer, now, link_info) < 0) - return 0; - - gbproxy_reset_imsi_acquisition(link_info); - - /* This message is most probably the response to the ident - * request sent by gbproxy_acquire_imsi(). Don't forward it to - * the SGSN. */ - return !is_ident_resp; - } - - /* The message cannot be processed since the IMSI is still missing */ - - /* If queue is getting too large, drop oldest msgb before adding new one */ - if (peer->cfg->stored_msgs_max_len > 0) { - int exceeded_max_len = link_info->stored_msgs_len - + 1 - peer->cfg->stored_msgs_max_len; - - for (; exceeded_max_len > 0; exceeded_max_len--) { - struct msgb *msgb_drop; - msgb_drop = msgb_dequeue_count(&link_info->stored_msgs, - &link_info->stored_msgs_len); - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) Dropping stored msgb from list " - "(!acq imsi, length %d, max_len exceeded)\n", - msgb_nsei(msgb_drop), link_info->stored_msgs_len); - - msgb_free(msgb_drop); - } - } - - /* Enqueue unpatched messages */ - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition in progress, " - "storing message (%s)\n", - msgb_nsei(msg), - parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); - - stored_msg = bssgp_msgb_copy(msg, "process_bssgp_ul"); - msgb_enqueue_count(&link_info->stored_msgs, stored_msg, - &link_info->stored_msgs_len); - - if (!link_info->imsi_acq_pending) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI is required but not available, " - "initiating identification procedure (%s)\n", - msgb_nsei(msg), - parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); - - gbproxy_acquire_imsi(peer, link_info, msgb_bvci(msg)); - - /* There is no explicit retransmission handling, the - * implementation relies on the MS doing proper retransmissions - * of the triggering message instead */ - - link_info->imsi_acq_pending = true; - } - - return 0; -} - -struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg, - struct msgb *msg, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_peer *peer = NULL; - - if (msgb_bvci(msg) >= 2) - peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); - - if (!peer && !parse_ctx->to_bss) - peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); - - if (!peer) - peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp); - - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(%s) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN", - parse_ctx->pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); - } - return peer; -} - -/* patch BSSGP message */ -static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gprs_gb_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct timespec ts = {0,}; - struct gbproxy_link_info *link_info = NULL; - uint32_t sgsn_nsei = cfg->nsip_sgsn_nsei; - - if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn && - !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) - return 1; - - parse_ctx.to_bss = 0; - parse_ctx.peer_nsei = msgb_nsei(msg); - - /* Parse BSSGP/LLC */ - rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc && !parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(BSS) patching: failed to parse invalid %s message\n", - msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); - gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(BSS) invalid message was: %s\n", - msgb_nsei(msg), msgb_hexdump(msg)); - return 0; - } - - /* Get peer */ - if (!peer) - peer = gbproxy_find_peer(cfg, msg, &parse_ctx); - - if (!peer) - return 0; - - - osmo_clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, - parse_ctx.llc_msg_name); - - gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); - - link_info = gbproxy_update_link_state_ul(peer, now, &parse_ctx); - - if (parse_ctx.g48_hdr) { - switch (parse_ctx.g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); - break; - case GSM48_MT_GMM_DETACH_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_REQS]); - break; - case GSM48_MT_GMM_ATTACH_COMPL: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_COMPLS]); - break; - case GSM48_MT_GMM_RA_UPD_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REQS]); - break; - case GSM48_MT_GMM_RA_UPD_COMPL: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_COMPLS]); - break; - case GSM48_MT_GMM_STATUS: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_BSS]); - break; - case GSM48_MT_GSM_ACT_PDP_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REQS]); - break; - case GSM48_MT_GSM_DEACT_PDP_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_REQS]); - break; - - default: - break; - } - } - - if (link_info && cfg->route_to_sgsn2) { - if (cfg->acquire_imsi && link_info->imsi_len == 0) - sgsn_nsei = 0xffff; - else if (gbproxy_imsi_matches(cfg, GBPROX_MATCH_ROUTING, - link_info)) - sgsn_nsei = cfg->nsip_sgsn2_nsei; - } - - if (link_info) - link_info->sgsn_nsei = sgsn_nsei; - - /* Handle IMSI acquisition */ - if (cfg->acquire_imsi) { - rc = gbproxy_imsi_acquisition(peer, msg, now, link_info, - &parse_ctx); - if (rc <= 0) - return rc; - } - - gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, link_info, &len_change, &parse_ctx); - - gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); - - if (sgsn_nsei != cfg->nsip_sgsn_nsei) { - /* Send message directly to the selected SGSN */ - rc = gbprox_relay2sgsn(cfg, msg, msgb_bvci(msg), sgsn_nsei); - /* Don't let the calling code handle the transmission */ - return 0; - } - - return 1; -} - -/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */ -static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gprs_gb_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct timespec ts = {0,}; - struct gbproxy_link_info *link_info = NULL; - - if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn && - !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) - return; - - parse_ctx.to_bss = 1; - parse_ctx.peer_nsei = msgb_nsei(msg); - - rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc && !parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) patching: failed to parse invalid %s message\n", - msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); - gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(SGSN) invalid message was: %s\n", - msgb_nsei(msg), msgb_hexdump(msg)); - return; - } - - /* Get peer */ - if (!peer) - peer = gbproxy_find_peer(cfg, msg, &parse_ctx); - - if (!peer) - return; - - osmo_clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - if (parse_ctx.g48_hdr) { - switch (parse_ctx.g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_ACKS]); - break; - case GSM48_MT_GMM_ATTACH_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); - break; - case GSM48_MT_GMM_DETACH_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_ACKS]); - break; - case GSM48_MT_GMM_RA_UPD_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_ACKS]); - break; - case GSM48_MT_GMM_RA_UPD_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REJS]); - break; - case GSM48_MT_GMM_STATUS: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_SGSN]); - break; - case GSM48_MT_GSM_ACT_PDP_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_ACKS]); - break; - case GSM48_MT_GSM_ACT_PDP_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REJS]); - break; - case GSM48_MT_GSM_DEACT_PDP_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_ACKS]); - break; - - default: - break; - } - } - - gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); - - link_info = gbproxy_update_link_state_dl(peer, now, &parse_ctx); - - gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, link_info, &len_change, &parse_ctx); - - gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); - - return; -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, - uint16_t ns_bvci, uint16_t sgsn_nsei) -{ - /* create a copy of the message so the old one can - * be free()d safely when we return from gbprox_rcvmsg() */ - struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2sgsn"); - int rc; - - DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, sgsn_nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = sgsn_nsei; - - strip_ns_hdr(msg); - - rc = gprs_ns_sendmsg(bssgp_nsi, msg); - if (rc < 0) - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]); - - return rc; -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, - uint16_t ns_bvci) -{ - /* create a copy of the message so the old one can - * be free()d safely when we return from gbprox_rcvmsg() */ - struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2peer"); - int rc; - - DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, peer->nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = peer->nsei; - - /* Strip the old NS header, it will be replaced with a new one */ - strip_ns_hdr(msg); - - rc = gprs_ns_sendmsg(bssgp_nsi, msg); - if (rc < 0) - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]); - - return rc; -} - -static int block_unblock_peer(struct gbproxy_config *cfg, uint16_t ptp_bvci, uint8_t pdu_type) -{ - struct gbproxy_peer *peer; - - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_bvci); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return -ENOENT; - } - - switch (pdu_type) { - case BSSGP_PDUT_BVC_BLOCK_ACK: - peer->blocked = true; - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_BLOCKED]); - break; - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - peer->blocked = false; - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_UNBLOCKED]); - break; - default: - break; - } - return 0; -} - -/* Send a message to a peer identified by ptp_bvci but using ns_bvci - * in the NS hdr */ -static int gbprox_relay2bvci(struct gbproxy_config *cfg, struct msgb *msg, uint16_t ptp_bvci, - uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_bvci); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return -ENOENT; - } - - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) -{ - return 0; -} - -/* Receive an incoming PTP message from a BSS-side NS-VC */ -static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t nsvci, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - int rc; - - peer = gbproxy_peer_by_bvci(cfg, ns_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_NOTICE, "Didn't find peer for " - "BVCI=%u for PTP message from NSVC=%u/NSEI=%u (BSS), " - "discarding message\n", - ns_bvci, nsvci, nsei); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ns_bvci, msg); - } - - check_peer_nsei(peer, nsei); - - rc = gbprox_process_bssgp_ul(cfg, msg, peer); - if (!rc) - return 0; - - switch (pdu_type) { - case BSSGP_PDUT_FLOW_CONTROL_BVC: - if (!cfg->route_to_sgsn2) - break; - - /* Send a copy to the secondary SGSN */ - gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); - break; - default: - break; - } - - - return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); -} - -/* Receive an incoming PTP message from a SGSN-side NS-VC */ -static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t nsvci, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - - peer = gbproxy_peer_by_bvci(cfg, ns_bvci); - - /* Send status messages before patching */ - - if (!peer) { - LOGP(DGPRS, LOGL_INFO, "Didn't find peer for " - "BVCI=%u for message from NSVC=%u/NSEI=%u (SGSN)\n", - ns_bvci, nsvci, nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ns_bvci, msg); - } - - if (peer->blocked) { - LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " - "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", - ns_bvci, nsvci, nsei); - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]); - return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg); - } - - switch (pdu_type) { - case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: - case BSSGP_PDUT_BVC_BLOCK_ACK: - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) - /* Hide ACKs from the secondary SGSN, the primary SGSN - * is responsible to send them. */ - return 0; - break; - default: - break; - } - - /* Optionally patch the message */ - gbprox_process_bssgp_dl(cfg, msg, peer); - - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -/* Receive an incoming signalling message from a BSS-side NS-VC */ -static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t ns_bvci) -{ - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - struct tlv_parsed tp; - uint8_t pdu_type = bgph->pdu_type; - int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); - struct gbproxy_peer *from_peer = NULL; - struct gprs_ra_id raid; - int copy_to_sgsn2 = 0; - int rc; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", - nsei, ns_bvci); - return -EINVAL; - } - - /* we actually should never see those two for BVCI == 0, but double-check - * just to make sure */ - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in " - "signalling\n", nsei); - return -EINVAL; - } - - bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_SUSPEND: - case BSSGP_PDUT_RESUME: - /* We implement RAI snooping during SUSPEND/RESUME, since it - * establishes a relationsip between BVCI/peer and the routeing - * area identification. The snooped information is then used - * for routing the {SUSPEND,RESUME}_[N]ACK back to the correct - * BSSGP */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) - goto err_mand_ie; - from_peer = gbproxy_peer_by_nsei(cfg, nsei); - if (!from_peer) - goto err_no_peer; - memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), - sizeof(from_peer->ra)); - gsm48_parse_ra(&raid, from_peer->ra); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME " - "RAI snooping: RAI %s behind BVCI=%u\n", - nsei, osmo_rai_name(&raid), from_peer->bvci); - /* FIXME: This only supports one BSS per RA */ - break; - case BSSGP_PDUT_BVC_RESET: - /* If we receive a BVC reset on the signalling endpoint, we - * don't want the SGSN to reset, as the signalling endpoint - * is common for all point-to-point BVCs (and thus all BTS) */ - if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { - uint16_t bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", - nsei, bvci); - if (bvci == 0) { - /* FIXME: only do this if SGSN is alive! */ - LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " - "BVC RESET ACK of BVCI=0\n", nsei); - return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, - nsei, 0, ns_bvci); - } - from_peer = gbproxy_peer_by_bvci(cfg, bvci); - if (!from_peer) { - /* if a PTP-BVC is reset, and we don't know that - * PTP-BVCI yet, we should allocate a new peer */ - LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for BVCI=%u via NSEI=%u\n", bvci, nsei); - from_peer = gbproxy_peer_alloc(cfg, bvci); - OSMO_ASSERT(from_peer); - from_peer->nsei = nsei; - } - - if (!check_peer_nsei(from_peer, nsei)) - from_peer->nsei = nsei; - - if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { - struct gprs_ra_id raid; - /* We have a Cell Identifier present in this - * PDU, this means we can extend our local - * state information about this particular cell - * */ - memcpy(from_peer->ra, - TLVP_VAL(&tp, BSSGP_IE_CELL_ID), - sizeof(from_peer->ra)); - gsm48_parse_ra(&raid, from_peer->ra); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u Cell ID %s\n", - nsei, bvci, osmo_rai_name(&raid)); - } - if (cfg->route_to_sgsn2) - copy_to_sgsn2 = 1; - } - break; - } - - /* Normally, we can simply pass on all signalling messages from BSS to - * SGSN */ - rc = gbprox_process_bssgp_ul(cfg, msg, from_peer); - if (!rc) - return 0; - - if (copy_to_sgsn2) - gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); - - return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on NSEI\n", - nsei); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_NSEI]); - return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", - nsei); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); -} - -/* Receive paging request from SGSN, we need to relay to proper BSS */ -static int gbprox_rx_paging(struct gbproxy_config *cfg, struct msgb *msg, struct tlv_parsed *tp, - uint32_t nsei, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer = NULL; - int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN; - - LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", - nsei); - if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n", - bvci); - errctr = GBPROX_GLOB_CTR_OTHER_ERR; - } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by RAI to peer BVCI=%u\n", - peer ? peer->bvci : -1); - errctr = GBPROX_GLOB_CTR_INV_RAI; - } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - peer = gbproxy_peer_by_lai(cfg, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by LAI to peer BVCI=%u\n", - peer ? peer->bvci : -1); - errctr = GBPROX_GLOB_CTR_INV_LAI; - } else - LOGPC(DGPRS, LOGL_INFO, "\n"); - - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " - "unable to route, missing IE\n", nsei); - rate_ctr_inc(&cfg->ctrg->ctr[errctr]); - return -EINVAL; - } - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -/* Receive an incoming BVC-RESET message from the SGSN */ -static int rx_reset_from_sgsn(struct gbproxy_config *cfg, - struct msgb *orig_msg, - struct msgb *msg, struct tlv_parsed *tp, - uint32_t nsei, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - uint16_t ptp_bvci; - - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, - NULL, orig_msg); - } - ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - - if (ptp_bvci >= 2) { - /* A reset for a PTP BVC was received, forward it to its - * respective peer */ - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", - nsei, ptp_bvci); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ptp_bvci, orig_msg); - } - return gbprox_relay2peer(msg, peer, ns_bvci); - } - - /* A reset for the Signalling entity has been received - * from the SGSN. As the signalling BVCI is shared - * among all the BSS's that we multiplex, it needs to - * be relayed */ - llist_for_each_entry(peer, &cfg->bts_peers, list) - gbprox_relay2peer(msg, peer, ns_bvci); - - return 0; -} - -/* Receive an incoming signalling message from the SGSN-side NS-VC */ -static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg, - struct msgb *orig_msg, uint32_t nsei, - uint16_t ns_bvci) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(orig_msg); - struct tlv_parsed tp; - uint8_t pdu_type = bgph->pdu_type; - int data_len; - struct gbproxy_peer *peer; - uint16_t bvci; - struct msgb *msg; - int rc = 0; - int cause; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " - "signalling\n", nsei, ns_bvci); - /* FIXME: Send proper error message */ - return -EINVAL; - } - - /* we actually should never see those two for BVCI == 0, but double-check - * just to make sure */ - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in " - "signalling\n", nsei); - return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); - } - - msg = bssgp_msgb_copy(orig_msg, "rx_sig_from_sgsn"); - gbprox_process_bssgp_dl(cfg, msg, NULL); - /* Update message info */ - bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - data_len = msgb_bssgp_len(orig_msg) - sizeof(*bgph); - rc = bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_BVC_RESET: - rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci); - break; - case BSSGP_PDUT_BVC_RESET_ACK: - if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) - break; - /* fall through */ - case BSSGP_PDUT_FLUSH_LL: - /* simple case: BVCI IE is mandatory */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) - goto err_mand_ie; - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_PAGING_PS: - case BSSGP_PDUT_PAGING_CS: - /* process the paging request (LAI/RAI lookup) */ - rc = gbprox_rx_paging(cfg, msg, &tp, nsei, ns_bvci); - break; - case BSSGP_PDUT_STATUS: - /* Some exception has occurred */ - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(SGSN) BSSGP STATUS ", nsei); - if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { - LOGPC(DGPRS, LOGL_NOTICE, "\n"); - goto err_mand_ie; - } - cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE); - LOGPC(DGPRS, LOGL_NOTICE, - "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), - bssgp_cause_str(cause)); - if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - LOGPC(DGPRS, LOGL_NOTICE, "BVCI=%u\n", bvci); - - if (cause == BSSGP_CAUSE_UNKNOWN_BVCI) - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - } else - LOGPC(DGPRS, LOGL_NOTICE, "\n"); - break; - /* those only exist in the SGSN -> BSS direction */ - case BSSGP_PDUT_SUSPEND_ACK: - case BSSGP_PDUT_SUSPEND_NACK: - case BSSGP_PDUT_RESUME_ACK: - case BSSGP_PDUT_RESUME_NACK: - /* RAI IE is mandatory */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) - goto err_mand_ie; - peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA)); - if (!peer) - goto err_no_peer; - rc = gbprox_relay2peer(msg, peer, ns_bvci); - break; - case BSSGP_PDUT_BVC_BLOCK_ACK: - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) - goto err_mand_ie; - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - if (bvci == 0) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP " - "%sBLOCK_ACK for signalling BVCI ?!?\n", nsei, - pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); - /* should we send STATUS ? */ - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - } else { - /* Mark BVC as (un)blocked */ - block_unblock_peer(cfg, bvci, pdu_type); - } - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_SGSN_INVOKE_TRACE: - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN]); - rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, orig_msg); - break; - default: - LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type %s not supported\n", bssgp_pdu_str(pdu_type)); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); - break; - } - - msgb_free(msg); - - return rc; -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", - nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - msgb_free(msg); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAI\n", - nsei); - rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_RAI]); - msgb_free(msg); - return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, orig_msg); -} - -static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei) -{ - return nsei == cfg->nsip_sgsn_nsei || - (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei); -} - -/* Main input function for Gb proxy */ -int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, - uint16_t ns_bvci, uint16_t nsvci) -{ - int rc; - int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei); - - /* Only BVCI=0 messages need special treatment */ - if (ns_bvci == 0 || ns_bvci == 1) { - if (remote_end_is_sgsn) - rc = gbprox_rx_sig_from_sgsn(cfg, msg, nsei, ns_bvci); - else - rc = gbprox_rx_sig_from_bss(cfg, msg, nsei, ns_bvci); - } else { - /* All other BVCI are PTP */ - if (remote_end_is_sgsn) - rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei, nsvci, - ns_bvci); - else - rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei, nsvci, - ns_bvci); - } - - return rc; -} - -int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi) -{ - struct gprs_nsvc *nsvc; - - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (!nsvc->persistent) - continue; - gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); - } - return 0; -} - -/* Signal handler for signals from NS layer */ -int gbprox_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gbproxy_config *cfg = handler_data; - struct ns_signal_data *nssd = signal_data; - struct gprs_nsvc *nsvc = nssd->nsvc; - struct gbproxy_peer *peer; - int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei); - - if (subsys != SS_L_NS) - return 0; - - if (signal == S_NS_RESET && remote_end_is_sgsn) { - /* We have received a NS-RESET from the NSEI and NSVC - * of the SGSN. This might happen with SGSN that start - * their own NS-RESET procedure without waiting for our - * NS-RESET */ - nsvc->remote_end_is_sgsn = 1; - } - - if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) { - LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, " - "re-starting RESET procedure\n"); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]); - gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, - nsvc->nsei, nsvc->nsvci); - } - - if (!nsvc->remote_end_is_sgsn) { - /* from BSS to SGSN */ - peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei); - if (!peer) { - LOGP(DGPRS, LOGL_NOTICE, "signal '%s' for unknown peer NSEI=%u/NSVCI=%u\n", - get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci); - return 0; - } - switch (signal) { - case S_NS_RESET: - case S_NS_BLOCK: - if (!peer->blocked) - break; - LOGP(DGPRS, LOGL_NOTICE, "Converting '%s' from NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n", - get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci); - bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, - peer->bvci, 0); - break; - } - } else { - /* Forward this message to all NS-VC to BSS */ - struct gprs_ns_inst *nsi = cfg->nsi; - struct gprs_nsvc *next_nsvc; - - llist_for_each_entry(next_nsvc, &nsi->gprs_nsvcs, list) { - if (next_nsvc->remote_end_is_sgsn) - continue; - - /* Note that the following does not start the full - * procedures including timer based retransmissions. */ - switch (signal) { - case S_NS_RESET: - gprs_ns_tx_reset(next_nsvc, nssd->cause); - break; - case S_NS_BLOCK: - gprs_ns_tx_block(next_nsvc, nssd->cause); - break; - case S_NS_UNBLOCK: - gprs_ns_tx_unblock(next_nsvc); - break; - } - } - } - return 0; -} - -void gbprox_reset(struct gbproxy_config *cfg) -{ - struct gbproxy_peer *peer, *tmp; - - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) - gbproxy_peer_free(peer); - - rate_ctr_group_free(cfg->ctrg); - gbproxy_init_config(cfg); -} - -int gbproxy_init_config(struct gbproxy_config *cfg) -{ - struct timespec tp; - - INIT_LLIST_HEAD(&cfg->bts_peers); - cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0); - if (!cfg->ctrg) { - LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n"); - return -1; - } - osmo_clock_gettime(CLOCK_REALTIME, &tp); - - return 0; -} diff --git a/src/gbproxy/gb_proxy_ctrl.c b/src/gbproxy/gb_proxy_ctrl.c deleted file mode 100644 index 4b7c2f430..000000000 --- a/src/gbproxy/gb_proxy_ctrl.c +++ /dev/null @@ -1,98 +0,0 @@ -/* Control Interface Implementation for the Gb-proxy */ -/* - * (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * All Rights Reserved - * - * Author: Daniel Willmann - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/core/talloc.h> - - -#include <osmocom/gprs/gprs_bssgp.h> -#include <osmocom/gprs/gprs_ns.h> - -#include <osmocom/ctrl/control_if.h> -#include <osmocom/ctrl/control_cmd.h> -#include <osmocom/sgsn/gb_proxy.h> -#include <osmocom/sgsn/debug.h> - -extern vector ctrl_node_vec; - -static int get_nsvc_state(struct ctrl_cmd *cmd, void *data) -{ - struct gbproxy_config *cfg = data; - struct gprs_ns_inst *nsi = cfg->nsi; - struct gprs_nsvc *nsvc; - - cmd->reply = talloc_strdup(cmd, ""); - - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (nsvc == nsi->unknown_nsvc) - continue; - - cmd->reply = gprs_nsvc_state_append(cmd->reply, nsvc); - } - - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state"); - -static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data) -{ - struct gbproxy_config *cfg = data; - struct gbproxy_peer *peer; - - cmd->reply = talloc_strdup(cmd, ""); - - llist_for_each_entry(peer, &cfg->bts_peers, list) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, peer->ra); - - cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n", - peer->nsei, peer->bvci, - raid.mcc, raid.mnc, - raid.lac, raid.rac, - peer->blocked ? "BLOCKED" : "UNBLOCKED"); - } - - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_RO(gbproxy_state, "gbproxy-state"); - -static int get_num_peers(struct ctrl_cmd *cmd, void *data) -{ - struct gbproxy_config *cfg = data; - - cmd->reply = talloc_strdup(cmd, ""); - cmd->reply = talloc_asprintf_append(cmd->reply, "%u", llist_count(&cfg->bts_peers)); - - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(num_peers, "number-of-peers"); - -int gb_ctrl_cmds_install(void) -{ - int rc = 0; - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_nsvc_state); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_gbproxy_state); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_num_peers); - - return rc; -} diff --git a/src/gbproxy/gb_proxy_main.c b/src/gbproxy/gb_proxy_main.c deleted file mode 100644 index 4319fda63..000000000 --- a/src/gbproxy/gb_proxy_main.c +++ /dev/null @@ -1,393 +0,0 @@ -/* NS-over-IP proxy */ - -/* (C) 2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2010 by On-Waves - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> -#include <errno.h> -#include <signal.h> -#include <sys/fcntl.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <osmocom/core/application.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/stats.h> - -#include <osmocom/gprs/gprs_ns.h> -#include <osmocom/gprs/gprs_bssgp.h> - -#include <osmocom/sgsn/signal.h> -#include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/vty.h> -#include <osmocom/sgsn/gb_proxy.h> - -#include <osmocom/ctrl/control_vty.h> -#include <osmocom/ctrl/control_if.h> -#include <osmocom/ctrl/ports.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/telnet_interface.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/stats.h> -#include <osmocom/vty/ports.h> -#include <osmocom/vty/misc.h> - -#include "../../bscconfig.h" - -#define _GNU_SOURCE -#include <getopt.h> - -void *tall_sgsn_ctx; - -const char *openbsc_copyright = - "Copyright (C) 2010 Harald Welte and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -#define CONFIG_FILE_DEFAULT "osmo-gbproxy.cfg" -#define CONFIG_FILE_LEGACY "osmo_gbproxy.cfg" - -static char *config_file = NULL; -struct gbproxy_config *gbcfg; -static int daemonize = 0; - -/* Pointer to the SGSN peer */ -extern struct gbprox_peer *gbprox_peer_sgsn; - -/* call-back function for the NS protocol */ -static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, - struct msgb *msg, uint16_t bvci) -{ - int rc = 0; - - switch (event) { - case GPRS_NS_EVT_UNIT_DATA: - rc = gbprox_rcvmsg(gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci); - break; - default: - LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); - if (msg) - msgb_free(msg); - rc = -EIO; - break; - } - return rc; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - case SIGTERM: - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(1); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_sgsn_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -static void print_usage() -{ - printf("Usage: bsc_hack\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -c --config-file filename The config file to use [%s]\n", CONFIG_FILE_DEFAULT); - printf(" -s --disable-color\n"); - printf(" -T --timestamp Prefix every log line with a timestamp\n"); - printf(" -V --version. Print the version.\n"); - printf(" -e --log-level number. Set a global loglevel.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "debug", 1, 0, 'd' }, - { "daemonize", 0, 0, 'D' }, - { "config-file", 1, 0, 'c' }, - { "disable-color", 0, 0, 's' }, - { "timestamp", 0, 0, 'T' }, - { "version", 0, 0, 'V' }, - { "log-level", 1, 0, 'e' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "hd:Dc:sTVe:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'V': - print_version(1); - exit(0); - break; - default: - break; - } - } -} - -int gbproxy_vty_is_config_node(struct vty *vty, int node) -{ - switch (node) { - /* add items that are not config */ - case CONFIG_NODE: - return 0; - - default: - return 1; - } -} - -int gbproxy_vty_go_parent(struct vty *vty) -{ - switch (vty->node) { - case GBPROXY_NODE: - default: - if (gbproxy_vty_is_config_node(vty, vty->node)) - vty->node = CONFIG_NODE; - else - vty->node = ENABLE_NODE; - - vty->index = NULL; - } - - return vty->node; -} - -static struct vty_app_info vty_info = { - .name = "OsmoGbProxy", - .version = PACKAGE_VERSION, - .go_parent_cb = gbproxy_vty_go_parent, - .is_config_node = gbproxy_vty_is_config_node, -}; - -/* default categories */ -static struct log_info_cat gprs_categories[] = { - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, -}; - -static const struct log_info gprs_log_info = { - .filter_fn = gprs_log_filter_fn, - .cat = gprs_categories, - .num_cat = ARRAY_SIZE(gprs_categories), -}; - -static bool file_exists(const char *path) -{ - struct stat sb; - return stat(path, &sb) ? false : true; -} - -int main(int argc, char **argv) -{ - int rc; - struct ctrl_handle *ctrl; - - tall_sgsn_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); - msgb_talloc_ctx_init(tall_sgsn_ctx, 0); - vty_info.tall_ctx = tall_sgsn_ctx; - - signal(SIGINT, &signal_handler); - signal(SIGTERM, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - osmo_init_logging2(tall_sgsn_ctx, &gprs_log_info); - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(); - osmo_talloc_vty_add_cmds(); - osmo_stats_vty_add_cmds(); - gbproxy_vty_init(); - - handle_options(argc, argv); - - /* Backwards compatibility: for years, the default config file name was - * osmo_gbproxy.cfg. All other Osmocom programs use osmo-*.cfg with a - * dash. To be able to use the new config file name without breaking - * previous setups that might rely on the legacy default config file - * name, we need to look for the old config file if no -c option was - * passed AND no file exists with the new default file name. */ - if (!config_file) { - /* No -c option was passed */ - if (file_exists(CONFIG_FILE_LEGACY) - && !file_exists(CONFIG_FILE_DEFAULT)) - config_file = CONFIG_FILE_LEGACY; - else - config_file = CONFIG_FILE_DEFAULT; - } - - rate_ctr_init(tall_sgsn_ctx); - osmo_stats_init(tall_sgsn_ctx); - - bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_sgsn_ctx); - if (!bssgp_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - - gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config); - if (!gbcfg) { - LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\n"); - exit(1); - } - gbproxy_init_config(gbcfg); - gbcfg->nsi = bssgp_nsi; - gprs_ns_vty_init(bssgp_nsi); - gprs_ns_set_log_ss(DNS); - bssgp_set_log_ss(DBSSGP); - osmo_signal_register_handler(SS_L_NS, &gbprox_signal, gbcfg); - - rc = gbproxy_parse_config(config_file, gbcfg); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file '%s'\n", config_file); - exit(2); - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_sgsn_ctx, NULL, - vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); - if (rc < 0) - exit(1); - - /* Start control interface after getting config for - * ctrl_vty_get_bind_addr() */ - ctrl = ctrl_interface_setup_dynip(gbcfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_GBPROXY, NULL); - if (!ctrl) { - LOGP(DGPRS, LOGL_FATAL, "Failed to create CTRL interface.\n"); - exit(1); - } - - if (gb_ctrl_cmds_install() != 0) { - LOGP(DGPRS, LOGL_FATAL, "Failed to install CTRL commands.\n"); - exit(1); - } - - if (!gprs_nsvc_by_nsei(gbcfg->nsi, gbcfg->nsip_sgsn_nsei)) { - LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u " - "without creating that NSEI before\n", - gbcfg->nsip_sgsn_nsei); - exit(2); - } - - rc = gprs_ns_nsip_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); - exit(2); - } - - rc = gprs_ns_frgre_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " - "socket. Do you have CAP_NET_RAW?\n"); - exit(2); - } - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* Reset all the persistent NS-VCs that we've read from the config */ - gbprox_reset_persistent_nsvcs(bssgp_nsi); - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} diff --git a/src/gbproxy/gb_proxy_patch.c b/src/gbproxy/gb_proxy_patch.c deleted file mode 100644 index 6235b04f4..000000000 --- a/src/gbproxy/gb_proxy_patch.c +++ /dev/null @@ -1,465 +0,0 @@ -/* Gb-proxy message patching */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/sgsn/gb_proxy.h> - -#include <osmocom/sgsn/gprs_utils.h> -#include <osmocom/sgsn/gprs_gb_parse.h> - -#include <osmocom/sgsn/debug.h> - -#include <osmocom/gprs/protocol/gsm_08_18.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/gsm/apn.h> - -extern void *tall_sgsn_ctx; - -/* patch RA identifier in place */ -static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_peer *peer, - int to_bss, const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - struct osmo_plmn_id old_plmn; - struct gprs_ra_id raid; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_RAID_PATCHED_SGSN : - GBPROX_PEER_CTR_RAID_PATCHED_BSS; - - if (!state->local_plmn.mcc || !state->local_plmn.mnc) - return; - - gsm48_parse_ra(&raid, (uint8_t *)raid_enc); - - old_plmn = (struct osmo_plmn_id){ - .mcc = raid.mcc, - .mnc = raid.mnc, - .mnc_3_digits = raid.mnc_3_digits, - }; - - if (!to_bss) { - /* BSS -> SGSN */ - if (state->local_plmn.mcc) - raid.mcc = peer->cfg->core_plmn.mcc; - - if (state->local_plmn.mnc) { - raid.mnc = peer->cfg->core_plmn.mnc; - raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits; - } - } else { - /* SGSN -> BSS */ - if (state->local_plmn.mcc) - raid.mcc = state->local_plmn.mcc; - - if (state->local_plmn.mnc) { - raid.mnc = state->local_plmn.mnc; - raid.mnc_3_digits = state->local_plmn.mnc_3_digits; - } - } - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to %s: " - "%s-%d-%d -> %s\n", - log_text, - to_bss ? "BSS" : "SGSN", - osmo_plmn_name(&old_plmn), raid.lac, raid.rac, - osmo_rai_name(&raid)); - - gsm48_encode_ra(raid_enc, &raid); - rate_ctr_inc(&peer->ctrg->ctr[counter]); -} - -static void gbproxy_patch_apn_ie(struct msgb *msg, - uint8_t *apn_ie, size_t apn_ie_len, - struct gbproxy_peer *peer, - size_t *new_apn_ie_len, const char *log_text) -{ - struct apn_ie_hdr { - uint8_t iei; - uint8_t apn_len; - uint8_t apn[0]; - } *hdr = (void *)apn_ie; - - size_t apn_len = hdr->apn_len; - uint8_t *apn = hdr->apn; - - OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr)); - OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102); - - if (peer->cfg->core_apn_size == 0) { - char str1[110]; - /* Remove the IE */ - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: Removing APN '%s'\n", - log_text, - osmo_apn_to_str(str1, apn, apn_len)); - - *new_apn_ie_len = 0; - msgb_resize_area(msg, apn_ie, apn_ie_len, 0); - } else { - /* Resize the IE */ - char str1[110]; - char str2[110]; - - OSMO_ASSERT(peer->cfg->core_apn_size <= 100); - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: " - "Replacing APN '%s' -> '%s'\n", - log_text, - osmo_apn_to_str(str1, apn, apn_len), - osmo_apn_to_str(str2, peer->cfg->core_apn, - peer->cfg->core_apn_size)); - - *new_apn_ie_len = peer->cfg->core_apn_size + 2; - msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size); - memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size); - hdr->apn_len = peer->cfg->core_apn_size; - } - - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); -} - -static int gbproxy_patch_tlli(uint8_t *tlli_enc, - struct gbproxy_peer *peer, - uint32_t new_tlli, - int to_bss, const char *log_text) -{ - uint32_t tlli_be; - uint32_t tlli; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_TLLI_PATCHED_SGSN : - GBPROX_PEER_CTR_TLLI_PATCHED_BSS; - - memcpy(&tlli_be, tlli_enc, sizeof(tlli_be)); - tlli = ntohl(tlli_be); - - if (tlli == new_tlli) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, tlli, new_tlli); - - tlli_be = htonl(new_tlli); - memcpy(tlli_enc, &tlli_be, sizeof(tlli_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc, - struct gbproxy_peer *peer, - uint32_t new_ptmsi, - int to_bss, const char *log_text) -{ - uint32_t ptmsi_be; - uint32_t ptmsi; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN : - GBPROX_PEER_CTR_PTMSI_PATCHED_BSS; - memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be)); - ptmsi = ntohl(ptmsi_be); - - if (ptmsi == new_ptmsi) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, ptmsi, new_ptmsi); - - ptmsi_be = htonl(new_ptmsi); - memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, int *len_change, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int have_patched = 0; - int fcs; - struct gbproxy_config *cfg = peer->cfg; - - if (parse_ctx->ptmsi_enc && link_info && - !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - if (ptmsi != GSM_RESERVED_TMSI) { - if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "P-TMSI")) - have_patched = 1; - } else { - /* TODO: invalidate old RAI if present (see below) */ - } - } - - if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - OSMO_ASSERT(ptmsi); - if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "new P-TMSI")) - have_patched = 1; - } - - if (parse_ctx->raid_enc) { - gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) { - /* TODO: Patch to invalid if P-TMSI unknown. */ - gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->apn_ie && - cfg->core_apn && - !parse_ctx->to_bss && - gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) && - cfg->core_apn) { - size_t new_len; - gbproxy_patch_apn_ie(msg, - parse_ctx->apn_ie, parse_ctx->apn_ie_len, - peer, &new_len, parse_ctx->llc_msg_name); - *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; - - have_patched = 1; - } - - if (have_patched) { - llc_len += *len_change; - ghp->crc_length += *len_change; - - /* Fix FCS */ - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", - ghp->fcs, fcs); - - llc[llc_len - 3] = fcs & 0xff; - llc[llc_len - 2] = (fcs >> 8) & 0xff; - llc[llc_len - 1] = (fcs >> 16) & 0xff; - } - - return have_patched; -} - -/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */ -void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, int *len_change, - struct gprs_gb_parse_context *parse_ctx) -{ - const char *err_info = NULL; - int err_ctr = -1; - - if (parse_ctx->bssgp_raid_enc) - gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer, - parse_ctx->to_bss, "BSSGP"); - - if (parse_ctx->need_decryption && - (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) { - /* Patching LLC messages has been requested - * explicitly, but the message (including the - * type) is encrypted, so we possibly fail to - * patch the LLC part of the message. */ - err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; - err_info = "GMM message is encrypted"; - goto patch_error; - } - - if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) { - /* Happens with unknown (not cached) TLLI coming from - * the SGSN */ - /* TODO: What shall be done with the message in this case? */ - err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN; - err_info = "TLLI sent by the SGSN is unknown"; - goto patch_error; - } - - if (!link_info) - return; - - if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) { - uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli, - link_info, parse_ctx->to_bss); - - if (tlli) { - gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli, - parse_ctx->to_bss, "TLLI"); - parse_ctx->tlli = tlli; - } else { - /* Internal error */ - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - err_info = "Replacement TLLI is 0"; - goto patch_error; - } - } - - if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - if (ptmsi != GSM_RESERVED_TMSI) - gbproxy_patch_ptmsi( - parse_ctx->bssgp_ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI"); - } - - if (parse_ctx->llc) { - uint8_t *llc = parse_ctx->llc; - size_t llc_len = parse_ctx->llc_len; - int llc_len_change = 0; - - gbproxy_patch_llc(msg, llc, llc_len, peer, link_info, - &llc_len_change, parse_ctx); - /* Note that the APN might have been resized here, but no - * pointer int the parse_ctx will refer to an adress after the - * APN. So it's possible to patch first and do the TLLI - * handling afterwards. */ - - if (llc_len_change) { - llc_len += llc_len_change; - - /* Fix LLC IE len */ - /* TODO: This is a kludge, but the a pointer to the - * start of the IE is not available here */ - if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) { - /* most probably a one byte length */ - if (llc_len > 127) { - err_info = "Cannot increase size"; - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - goto patch_error; - } - llc[-1] = llc_len | 0x80; - } else { - llc[-2] = (llc_len >> 8) & 0x7f; - llc[-1] = llc_len & 0xff; - } - *len_change += llc_len_change; - } - /* Note that the tp struct might contain invalid pointers here - * if the LLC field has changed its size */ - parse_ctx->llc_len = llc_len; - } - return; - -patch_error: - OSMO_ASSERT(err_ctr >= 0); - rate_ctr_inc(&peer->ctrg->ctr[err_ctr]); - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n", - msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS", - err_info); -} - -void gbproxy_clear_patch_filter(struct gbproxy_match *match) -{ - if (match->enable) { - regfree(&match->re_comp); - match->enable = false; - } - talloc_free(match->re_str); - match->re_str = NULL; -} - -int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter, - const char **err_msg) -{ - static char err_buf[300]; - int rc; - - gbproxy_clear_patch_filter(match); - - if (!filter) - return 0; - - rc = regcomp(&match->re_comp, filter, - REG_EXTENDED | REG_NOSUB | REG_ICASE); - - if (rc == 0) { - match->enable = true; - match->re_str = talloc_strdup(tall_sgsn_ctx, filter); - return 0; - } - - if (err_msg) { - regerror(rc, &match->re_comp, - err_buf, sizeof(err_buf)); - *err_msg = err_buf; - } - - return -1; -} - -int gbproxy_check_imsi(struct gbproxy_match *match, - const uint8_t *imsi, size_t imsi_len) -{ - char mi_buf[200]; - int rc; - - if (!match->enable) - return 1; - - rc = gprs_is_mi_imsi(imsi, imsi_len); - if (rc > 0) - rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len); - if (rc <= 0) { - LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n", - osmo_hexdump(imsi, imsi_len)); - return -1; - } - - LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc); - - rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0); - if (rc == REG_NOMATCH) { - LOGP(DGPRS, LOGL_INFO, - "IMSI '%s' doesn't match pattern '%s'\n", - mi_buf, match->re_str); - return 0; - } - - return 1; -} diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c deleted file mode 100644 index 48482b6a1..000000000 --- a/src/gbproxy/gb_proxy_peer.c +++ /dev/null @@ -1,240 +0,0 @@ -/* Gb proxy peer handling */ - -/* (C) 2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2010-2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/sgsn/gb_proxy.h> - -#include <osmocom/sgsn/debug.h> - -#include <osmocom/gprs/protocol/gsm_08_18.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/stats.h> -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/tlv.h> - -#include <string.h> - -extern void *tall_sgsn_ctx; - -static const struct rate_ctr_desc peer_ctr_description[] = { - { "blocked", "BVC Block " }, - { "unblocked", "BVC Unblock " }, - { "dropped", "BVC blocked, dropped packet " }, - { "inv-nsei", "NSEI mismatch " }, - { "tx-err", "NS Transmission error " }, - { "raid-mod:bss", "RAID patched (BSS )" }, - { "raid-mod:sgsn", "RAID patched (SGSN)" }, - { "apn-mod:sgsn", "APN patched " }, - { "tlli-mod:bss", "TLLI patched (BSS )" }, - { "tlli-mod:sgsn", "TLLI patched (SGSN)" }, - { "ptmsi-mod:bss", "P-TMSI patched (BSS )" }, - { "ptmsi-mod:sgsn","P-TMSI patched (SGSN)" }, - { "mod-crypt-err", "Patch error: encrypted " }, - { "mod-err", "Patch error: other " }, - { "attach-reqs", "Attach Request count " }, - { "attach-rejs", "Attach Reject count " }, - { "attach-acks", "Attach Accept count " }, - { "attach-cpls", "Attach Completed count " }, - { "ra-upd-reqs", "RoutingArea Update Request count" }, - { "ra-upd-rejs", "RoutingArea Update Reject count " }, - { "ra-upd-acks", "RoutingArea Update Accept count " }, - { "ra-upd-cpls", "RoutingArea Update Compltd count" }, - { "gmm-status", "GMM Status count (BSS)" }, - { "gmm-status", "GMM Status count (SGSN)" }, - { "detach-reqs", "Detach Request count " }, - { "detach-acks", "Detach Accept count " }, - { "pdp-act-reqs", "PDP Activation Request count " }, - { "pdp-act-rejs", "PDP Activation Reject count " }, - { "pdp-act-acks", "PDP Activation Accept count " }, - { "pdp-deact-reqs","PDP Deactivation Request count " }, - { "pdp-deact-acks","PDP Deactivation Accept count " }, - { "tlli-unknown", "TLLI from SGSN unknown " }, - { "tlli-cache", "TLLI cache size " }, -}; - -osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described); - -static const struct rate_ctr_group_desc peer_ctrg_desc = { - .group_name_prefix = "gbproxy:peer", - .group_description = "GBProxy Peer Statistics", - .num_ctr = ARRAY_SIZE(peer_ctr_description), - .ctr_desc = peer_ctr_description, - .class_id = OSMO_STATS_CLASS_PEER, -}; - - -/* Find the gbprox_peer by its BVCI */ -struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (peer->bvci == bvci) - return peer; - } - return NULL; -} - -/* Find the gbprox_peer by its NSEI */ -struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg, - uint16_t nsei) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (peer->nsei == nsei) - return peer; - } - return NULL; -} - -/* look-up a peer by its Routeing Area Identification (RAI) */ -struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg, - const uint8_t *ra) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra, ra, 6)) - return peer; - } - return NULL; -} - -/* look-up a peer by its Location Area Identification (LAI) */ -struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg, - const uint8_t *la) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra, la, 5)) - return peer; - } - return NULL; -} - -/* look-up a peer by its Location Area Code (LAC) */ -struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg, - const uint8_t *la) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra + 3, la + 3, 2)) - return peer; - } - return NULL; -} - -struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg, - struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - uint16_t bvci; - - bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - if (bvci >= 2) - return gbproxy_peer_by_bvci(cfg, bvci); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - /* Only compare LAC part, since MCC/MNC are possibly patched. - * Since the LAC of different BSS must be different when - * MCC/MNC are patched, collisions shouldn't happen. */ - return gbproxy_peer_by_lac(cfg, rai); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA); - return gbproxy_peer_by_lac(cfg, lai); - } - - return NULL; -} - -static void clean_stale_timer_cb(void *data) -{ - time_t now; - struct timespec ts = {0,}; - struct gbproxy_peer *peer = (struct gbproxy_peer *) data; - - osmo_clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - gbproxy_remove_stale_link_infos(peer, now); - if (peer->cfg->clean_stale_timer_freq != 0) - osmo_timer_schedule(&peer->clean_stale_timer, - peer->cfg->clean_stale_timer_freq, 0); -} - -struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci) -{ - struct gbproxy_peer *peer; - - peer = talloc_zero(tall_sgsn_ctx, struct gbproxy_peer); - if (!peer) - return NULL; - - peer->bvci = bvci; - peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci); - if (!peer->ctrg) { - talloc_free(peer); - return NULL; - } - peer->cfg = cfg; - - llist_add(&peer->list, &cfg->bts_peers); - - INIT_LLIST_HEAD(&peer->patch_state.logical_links); - - osmo_timer_setup(&peer->clean_stale_timer, clean_stale_timer_cb, peer); - if (peer->cfg->clean_stale_timer_freq != 0) - osmo_timer_schedule(&peer->clean_stale_timer, - peer->cfg->clean_stale_timer_freq, 0); - - return peer; -} - -void gbproxy_peer_free(struct gbproxy_peer *peer) -{ - llist_del(&peer->list); - osmo_timer_del(&peer->clean_stale_timer); - gbproxy_delete_link_infos(peer); - - rate_ctr_group_free(peer->ctrg); - peer->ctrg = NULL; - - talloc_free(peer); -} - -int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci) -{ - int counter = 0; - struct gbproxy_peer *peer, *tmp; - - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) { - if (peer->nsei != nsei) - continue; - if (bvci && peer->bvci != bvci) - continue; - - gbproxy_peer_free(peer); - counter += 1; - } - - return counter; -} diff --git a/src/gbproxy/gb_proxy_tlli.c b/src/gbproxy/gb_proxy_tlli.c deleted file mode 100644 index 4e21ede86..000000000 --- a/src/gbproxy/gb_proxy_tlli.c +++ /dev/null @@ -1,723 +0,0 @@ -/* Gb-proxy TLLI state handling */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/gsm/gsm48.h> - -#include <osmocom/sgsn/gb_proxy.h> - -#include <osmocom/sgsn/gprs_utils.h> -#include <osmocom/sgsn/gprs_gb_parse.h> - -#include <osmocom/sgsn/debug.h> - -#include <osmocom/gsm/gsm_utils.h> - -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/talloc.h> - -struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer, - uint32_t tlli) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->tlli.current == tlli || - link_info->tlli.assigned == tlli) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_ptmsi( - struct gbproxy_peer *peer, - uint32_t ptmsi) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (ptmsi == GSM_RESERVED_TMSI) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->tlli.ptmsi == ptmsi) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - /* Don't care about the NSEI */ - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->sgsn_tlli.current == tlli || - link_info->sgsn_tlli.assigned == tlli) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli, uint32_t sgsn_nsei) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if ((link_info->sgsn_tlli.current == tlli || - link_info->sgsn_tlli.assigned == tlli) && - link_info->sgsn_nsei == sgsn_nsei) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_imsi( - struct gbproxy_peer *peer, - const uint8_t *imsi, - size_t imsi_len) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!gprs_is_mi_imsi(imsi, imsi_len)) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) { - if (link_info->imsi_len != imsi_len) - continue; - if (memcmp(link_info->imsi, imsi, imsi_len) != 0) - continue; - - return link_info; - } - - return NULL; -} - -void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info) -{ - struct msgb *msg, *nxt; - - llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) { - llist_del(&msg->list); - msgb_free(msg); - } -} - -void gbproxy_delete_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - gbproxy_link_info_discard_messages(link_info); - - llist_del(&link_info->list); - talloc_free(link_info); - state->logical_link_count -= 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -void gbproxy_delete_link_infos(struct gbproxy_peer *peer) -{ - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state = &peer->patch_state; - - llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) - gbproxy_delete_link_info(peer, link_info); - - OSMO_ASSERT(state->logical_link_count == 0); - OSMO_ASSERT(llist_empty(&state->logical_links)); -} - -void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - link_info->timestamp = now; - llist_add(&link_info->list, &state->logical_links); - state->logical_link_count += 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - int exceeded_max_len = 0; - int deleted_count = 0; - int check_for_age; - - if (peer->cfg->tlli_max_len > 0) - exceeded_max_len = - state->logical_link_count - peer->cfg->tlli_max_len; - - check_for_age = peer->cfg->tlli_max_age > 0; - - for (; exceeded_max_len > 0; exceeded_max_len--) { - struct gbproxy_link_info *link_info; - OSMO_ASSERT(!llist_empty(&state->logical_links)); - link_info = llist_entry(state->logical_links.prev, - struct gbproxy_link_info, - list); - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list " - "(stale, length %d, max_len exceeded)\n", - link_info->tlli.current, state->logical_link_count); - - gbproxy_delete_link_info(peer, link_info); - deleted_count += 1; - } - - while (check_for_age && !llist_empty(&state->logical_links)) { - time_t age; - struct gbproxy_link_info *link_info; - link_info = llist_entry(state->logical_links.prev, - struct gbproxy_link_info, - list); - age = now - link_info->timestamp; - /* age < 0 only happens after system time jumps, discard entry */ - if (age <= peer->cfg->tlli_max_age && age >= 0) { - check_for_age = 0; - continue; - } - - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list " - "(stale, age %d, max_age exceeded)\n", - link_info->tlli.current, (int)age); - - gbproxy_delete_link_info(peer, link_info); - deleted_count += 1; - } - - return deleted_count; -} - -struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer) -{ - struct gbproxy_link_info *link_info; - - link_info = talloc_zero(peer, struct gbproxy_link_info); - link_info->tlli.ptmsi = GSM_RESERVED_TMSI; - link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI; - - link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; - - INIT_LLIST_HEAD(&link_info->stored_msgs); - - return link_info; -} - -void gbproxy_detach_link_info( - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - llist_del(&link_info->list); - OSMO_ASSERT(state->logical_link_count > 0); - state->logical_link_count -= 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -void gbproxy_update_link_info(struct gbproxy_link_info *link_info, - const uint8_t *imsi, size_t imsi_len) -{ - if (!gprs_is_mi_imsi(imsi, imsi_len)) - return; - - link_info->imsi_len = imsi_len; - link_info->imsi = - talloc_realloc_size(link_info, link_info->imsi, imsi_len); - OSMO_ASSERT(link_info->imsi != NULL); - memcpy(link_info->imsi, imsi, imsi_len); -} - -void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state, - struct gbproxy_peer *peer, uint32_t new_tlli) -{ - if (new_tlli == tlli_state->current) - return; - - LOGP(DGPRS, LOGL_INFO, - "The TLLI has been reassigned from %08x to %08x\n", - tlli_state->current, new_tlli); - - /* Remember assigned TLLI */ - tlli_state->assigned = new_tlli; - tlli_state->bss_validated = false; - tlli_state->net_validated = false; -} - -uint32_t gbproxy_map_tlli(uint32_t other_tlli, - struct gbproxy_link_info *link_info, int to_bss) -{ - uint32_t tlli = 0; - struct gbproxy_tlli_state *src, *dst; - if (to_bss) { - src = &link_info->sgsn_tlli; - dst = &link_info->tlli; - } else { - src = &link_info->tlli; - dst = &link_info->sgsn_tlli; - } - if (src->current == other_tlli) - tlli = dst->current; - else if (src->assigned == other_tlli) - tlli = dst->assigned; - - return tlli; -} - -static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state, - uint32_t tlli, int to_bss) -{ - LOGP(DGPRS, LOGL_DEBUG, - "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n", - __func__, tlli_state->current, tlli_state->assigned, - tlli_state->net_validated, tlli_state->bss_validated, tlli); - - if (!tlli_state->assigned || tlli_state->assigned != tlli) - return; - - /* TODO: Is this ok? Check spec */ - if (gprs_tlli_type(tlli) != TLLI_LOCAL) - return; - - /* See GSM 04.08, 4.7.1.5 */ - if (to_bss) - tlli_state->net_validated = true; - else - tlli_state->bss_validated = true; - - if (!tlli_state->bss_validated || !tlli_state->net_validated) - return; - - LOGP(DGPRS, LOGL_INFO, - "The TLLI %08x has been validated (was %08x)\n", - tlli_state->assigned, tlli_state->current); - - tlli_state->current = tlli; - tlli_state->assigned = 0; -} - -static void gbproxy_touch_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - time_t now) -{ - gbproxy_detach_link_info(peer, link_info); - gbproxy_attach_link_info(peer, now, link_info); -} - -static int gbproxy_unregister_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - if (!link_info) - return 1; - - if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) { - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n", - link_info->tlli.current); - gbproxy_delete_link_info(peer, link_info); - return 1; - } - - link_info->tlli.current = 0; - link_info->tlli.assigned = 0; - link_info->sgsn_tlli.current = 0; - link_info->sgsn_tlli.assigned = 0; - - link_info->is_deregistered = true; - - gbproxy_reset_link(link_info); - - return 0; -} - -int gbproxy_imsi_matches(struct gbproxy_config *cfg, - enum gbproxy_match_id match_id, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_match *match; - OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches)); - - match = &cfg->matches[match_id]; - if (!match->enable) - return 1; - - return link_info != NULL && link_info->is_matching[match_id]; -} - -static void gbproxy_assign_imsi(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - struct gprs_gb_parse_context *parse_ctx) -{ - int imsi_matches; - struct gbproxy_link_info *other_link_info; - enum gbproxy_match_id match_id; - - /* Make sure that there is a second entry with the same IMSI */ - other_link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - - if (other_link_info && other_link_info != link_info) { - char mi_buf[200]; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - parse_ctx->imsi, parse_ctx->imsi_len); - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (IMSI %s re-used)\n", - other_link_info->tlli.current, mi_buf); - gbproxy_delete_link_info(peer, other_link_info); - } - - /* Update the IMSI field */ - gbproxy_update_link_info(link_info, - parse_ctx->imsi, parse_ctx->imsi_len); - - /* Check, whether the IMSI matches */ - OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) == - ARRAY_SIZE(peer->cfg->matches)); - for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching); - ++match_id) { - imsi_matches = gbproxy_check_imsi( - &peer->cfg->matches[match_id], - parse_ctx->imsi, parse_ctx->imsi_len); - if (imsi_matches >= 0) - link_info->is_matching[match_id] = imsi_matches ? true : false; - } -} - -static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a, - const struct gbproxy_tlli_state *b) -{ - if (a->current && a->current == b->current) - return 1; - - if (a->assigned && a->assigned == b->assigned) - return 1; - - if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi) - return 1; - - return 0; -} - -static void gbproxy_remove_matching_link_infos( - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) -{ - struct gbproxy_link_info *info, *nxt; - struct gbproxy_patch_state *state = &peer->patch_state; - - /* Make sure that there is no second entry with the same P-TMSI or TLLI */ - llist_for_each_entry_safe(info, nxt, &state->logical_links, list) { - if (info == link_info) - continue; - - if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) && - (link_info->sgsn_nsei != info->sgsn_nsei || - !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli))) - continue; - - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n", - info->tlli.current); - gbproxy_delete_link_info(peer, info); - } -} - -static struct gbproxy_link_info *gbproxy_get_link_info_ul( - struct gbproxy_peer *peer, - int *tlli_is_valid, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - if (parse_ctx->tlli_enc) { - link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli); - - if (link_info) { - *tlli_is_valid = 1; - return link_info; - } - } - - *tlli_is_valid = 0; - - if (!link_info && parse_ctx->imsi) { - link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - } - - if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) { - uint32_t bss_ptmsi; - gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi); - link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi); - } - - if (!link_info) - return NULL; - - link_info->is_deregistered = false; - - return link_info; -} - -struct gbproxy_link_info *gbproxy_update_link_state_ul( - struct gbproxy_peer *peer, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info; - int tlli_is_valid; - - link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx); - - if (parse_ctx->tlli_enc && parse_ctx->llc) { - uint32_t sgsn_tlli; - - if (!link_info) { - LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", - parse_ctx->tlli); - link_info = gbproxy_link_info_alloc(peer); - gbproxy_attach_link_info(peer, now, link_info); - - /* Setup TLLIs */ - sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - link_info->sgsn_tlli.current = sgsn_tlli; - link_info->tlli.current = parse_ctx->tlli; - } else if (!tlli_is_valid) { - /* New TLLI (info found by IMSI or P-TMSI) */ - link_info->tlli.current = parse_ctx->tlli; - link_info->tlli.assigned = 0; - link_info->sgsn_tlli.current = - gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - link_info->sgsn_tlli.assigned = 0; - gbproxy_touch_link_info(peer, link_info, now); - } else { - sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0); - if (!sgsn_tlli) - sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - - gbproxy_validate_tlli(&link_info->tlli, - parse_ctx->tlli, 0); - gbproxy_validate_tlli(&link_info->sgsn_tlli, - sgsn_tlli, 0); - gbproxy_touch_link_info(peer, link_info, now); - } - } else if (link_info) { - gbproxy_touch_link_info(peer, link_info, now); - } - - if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) - gbproxy_assign_imsi(peer, link_info, parse_ctx); - - return link_info; -} - -static struct gbproxy_link_info *gbproxy_get_link_info_dl( - struct gbproxy_peer *peer, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - /* Which key to use depends on its availability only, if that fails, do - * not retry it with another key (e.g. IMSI). */ - if (parse_ctx->tlli_enc) - link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli, - parse_ctx->peer_nsei); - - /* TODO: Get link_info by (SGSN) P-TMSI if that is available (see - * GSM 08.18, 7.2) instead of using the IMSI as key. */ - else if (parse_ctx->imsi) - link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - - if (link_info) - link_info->is_deregistered = false; - - return link_info; -} - -struct gbproxy_link_info *gbproxy_update_link_state_dl( - struct gbproxy_peer *peer, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - link_info = gbproxy_get_link_info_dl(peer, parse_ctx); - - if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) { - /* A new P-TMSI has been signalled in the message, - * register new TLLI */ - uint32_t new_sgsn_ptmsi; - uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi); - - if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi) - new_bss_ptmsi = link_info->tlli.ptmsi; - - if (new_bss_ptmsi == GSM_RESERVED_TMSI) - new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi); - - LOGP(DGPRS, LOGL_INFO, - "Got new PTMSI %08x from SGSN, using %08x for BSS\n", - new_sgsn_ptmsi, new_bss_ptmsi); - /* Setup PTMSIs */ - link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi; - link_info->tlli.ptmsi = new_bss_ptmsi; - } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info && - !peer->cfg->patch_ptmsi) { - /* A new P-TMSI has been signalled in the message with an unknown - * TLLI, create a new link_info */ - /* TODO: Add a test case for this branch */ - uint32_t new_ptmsi; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - - LOGP(DGPRS, LOGL_INFO, - "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n", - parse_ctx->tlli, new_ptmsi); - - link_info = gbproxy_link_info_alloc(peer); - link_info->sgsn_tlli.current = parse_ctx->tlli; - link_info->tlli.current = parse_ctx->tlli; - link_info->sgsn_tlli.ptmsi = new_ptmsi; - link_info->tlli.ptmsi = new_ptmsi; - gbproxy_attach_link_info(peer, now, link_info); - } else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info && - !peer->cfg->patch_ptmsi) { - /* Unknown SGSN TLLI, create a new link_info */ - uint32_t new_ptmsi; - link_info = gbproxy_link_info_alloc(peer); - LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n", - parse_ctx->tlli); - - gbproxy_attach_link_info(peer, now, link_info); - - /* Setup TLLIs */ - link_info->sgsn_tlli.current = parse_ctx->tlli; - link_info->tlli.current = parse_ctx->tlli; - - if (!parse_ctx->new_ptmsi_enc) - return link_info; - /* A new P-TMSI has been signalled in the message */ - - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - LOGP(DGPRS, LOGL_INFO, - "Assigning new P-TMSI %08x\n", new_ptmsi); - /* Setup P-TMSIs */ - link_info->sgsn_tlli.ptmsi = new_ptmsi; - link_info->tlli.ptmsi = new_ptmsi; - } else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) { - uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli, - link_info, 1); - gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1); - gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1); - gbproxy_touch_link_info(peer, link_info, now); - } else if (link_info) { - gbproxy_touch_link_info(peer, link_info, now); - } - - if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) - gbproxy_assign_imsi(peer, link_info, parse_ctx); - - return link_info; -} - -int gbproxy_update_link_state_after( - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - int rc = 0; - if (parse_ctx->invalidate_tlli && link_info) { - int keep_info = - peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS || - (peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH && - parse_ctx->await_reattach) || - (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED && - link_info->imsi_len > 0); - if (keep_info) { - LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n", - link_info->tlli.current); - rc = gbproxy_unregister_link_info(peer, link_info); - } else { - LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n", - link_info->tlli.current); - gbproxy_delete_link_info(peer, link_info); - rc = 1; - } - } else if (parse_ctx->to_bss && parse_ctx->tlli_enc && - parse_ctx->new_ptmsi_enc && link_info) { - /* A new PTMSI has been signaled in the message, - * register new TLLI */ - uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi; - uint32_t new_bss_ptmsi = link_info->tlli.ptmsi; - uint32_t new_sgsn_tlli; - uint32_t new_bss_tlli = 0; - - new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL); - if (new_bss_ptmsi != GSM_RESERVED_TMSI) - new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL); - LOGP(DGPRS, LOGL_INFO, - "Assigning new TLLI %08x to SGSN, %08x to BSS\n", - new_sgsn_tlli, new_bss_tlli); - - gbproxy_reassign_tlli(&link_info->sgsn_tlli, - peer, new_sgsn_tlli); - gbproxy_reassign_tlli(&link_info->tlli, - peer, new_bss_tlli); - gbproxy_remove_matching_link_infos(peer, link_info); - } - - gbproxy_remove_stale_link_infos(peer, now); - - return rc; -} - - diff --git a/src/gbproxy/gb_proxy_vty.c b/src/gbproxy/gb_proxy_vty.c deleted file mode 100644 index 5c4f45420..000000000 --- a/src/gbproxy/gb_proxy_vty.c +++ /dev/null @@ -1,926 +0,0 @@ -/* - * (C) 2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2010 by On-Waves - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <string.h> -#include <time.h> -#include <inttypes.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/gsm/gsm48.h> - -#include <osmocom/gprs/gprs_ns.h> -#include <osmocom/gsm/apn.h> - -#include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gb_proxy.h> -#include <osmocom/sgsn/gprs_utils.h> -#include <osmocom/sgsn/vty.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/vty.h> -#include <osmocom/vty/misc.h> - -static struct gbproxy_config *g_cfg = NULL; - -/* - * vty code for gbproxy below - */ -static struct cmd_node gbproxy_node = { - GBPROXY_NODE, - "%s(config-gbproxy)# ", - 1, -}; - -static const struct value_string keep_modes[] = { - {GBPROX_KEEP_NEVER, "never"}, - {GBPROX_KEEP_REATTACH, "re-attach"}, - {GBPROX_KEEP_IDENTIFIED, "identified"}, - {GBPROX_KEEP_ALWAYS, "always"}, - {0, NULL} -}; - -static const struct value_string match_ids[] = { - {GBPROX_MATCH_PATCHING, "patching"}, - {GBPROX_MATCH_ROUTING, "routing"}, - {0, NULL} -}; - -static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer) -{ - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, peer->ra); - - vty_out(vty, "NSEI %5u, PTP-BVCI %5u, " - "RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid)); - if (peer->blocked) - vty_out(vty, " [BVC-BLOCKED]"); - - vty_out(vty, "%s", VTY_NEWLINE); -} - -static int config_write_gbproxy(struct vty *vty) -{ - enum gbproxy_match_id match_id; - - vty_out(vty, "gbproxy%s", VTY_NEWLINE); - - vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, - VTY_NEWLINE); - - if (g_cfg->core_plmn.mcc > 0) - vty_out(vty, " core-mobile-country-code %s%s", - osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE); - if (g_cfg->core_plmn.mnc > 0) - vty_out(vty, " core-mobile-network-code %s%s", - osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE); - - for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) { - struct gbproxy_match *match = &g_cfg->matches[match_id]; - if (match->re_str) - vty_out(vty, " match-imsi %s %s%s", - get_value_string(match_ids, match_id), - match->re_str, VTY_NEWLINE); - } - - if (g_cfg->core_apn != NULL) { - if (g_cfg->core_apn_size > 0) { - char str[500] = {0}; - vty_out(vty, " core-access-point-name %s%s", - osmo_apn_to_str(str, g_cfg->core_apn, - g_cfg->core_apn_size), - VTY_NEWLINE); - } else { - vty_out(vty, " core-access-point-name none%s", - VTY_NEWLINE); - } - } - - if (g_cfg->route_to_sgsn2) - vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei, - VTY_NEWLINE); - - if (g_cfg->clean_stale_timer_freq > 0) - vty_out(vty, " link-list clean-stale-timer %u%s", - g_cfg->clean_stale_timer_freq, VTY_NEWLINE); - if (g_cfg->tlli_max_age > 0) - vty_out(vty, " link-list max-age %d%s", - g_cfg->tlli_max_age, VTY_NEWLINE); - if (g_cfg->tlli_max_len > 0) - vty_out(vty, " link-list max-length %d%s", - g_cfg->tlli_max_len, VTY_NEWLINE); - vty_out(vty, " link-list keep-mode %s%s", - get_value_string(keep_modes, g_cfg->keep_link_infos), - VTY_NEWLINE); - if (g_cfg->stored_msgs_max_len > 0) - vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s", - g_cfg->stored_msgs_max_len, VTY_NEWLINE); - - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy, - cfg_gbproxy_cmd, - "gbproxy", - "Configure the Gb proxy") -{ - vty->node = GBPROXY_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_nsip_sgsn_nsei, - cfg_nsip_sgsn_nsei_cmd, - "sgsn nsei <0-65534>", - "SGSN information\n" - "NSEI to be used in the connection with the SGSN\n" - "The NSEI\n") -{ - unsigned int nsei = atoi(argv[0]); - - if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) { - vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->nsip_sgsn_nsei = nsei; - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n" - -DEFUN(cfg_gbproxy_core_mnc, - cfg_gbproxy_core_mnc_cmd, - "core-mobile-network-code <1-999>", - GBPROXY_CORE_MNC_STR "NCC value\n") -{ - uint16_t mnc; - bool mnc_3_digits; - if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) { - vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - g_cfg->core_plmn.mnc = mnc; - g_cfg->core_plmn.mnc_3_digits = mnc_3_digits; - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_core_mnc, - cfg_gbproxy_no_core_mnc_cmd, - "no core-mobile-network-code", - NO_STR GBPROXY_CORE_MNC_STR) -{ - g_cfg->core_plmn.mnc = 0; - g_cfg->core_plmn.mnc_3_digits = false; - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n" - -DEFUN(cfg_gbproxy_core_mcc, - cfg_gbproxy_core_mcc_cmd, - "core-mobile-country-code <1-999>", - GBPROXY_CORE_MCC_STR "MCC value\n") -{ - g_cfg->core_plmn.mcc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_core_mcc, - cfg_gbproxy_no_core_mcc_cmd, - "no core-mobile-country-code", - NO_STR GBPROXY_CORE_MCC_STR) -{ - g_cfg->core_plmn.mcc = 0; - return CMD_SUCCESS; -} - -#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n" - -DEFUN(cfg_gbproxy_match_imsi, - cfg_gbproxy_match_imsi_cmd, - "match-imsi (patching|routing) .REGEXP", - GBPROXY_MATCH_IMSI_STR - "Patch MS related information elements on match only\n" - "Route to the secondary SGSN on match only\n" - "Regular expression for the IMSI match\n") -{ - const char *filter = argv[1]; - const char *err_msg = NULL; - struct gbproxy_match *match; - enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]); - - OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && - match_id < GBPROX_MATCH_LAST); - match = &g_cfg->matches[match_id]; - - if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { - vty_out(vty, "Match expression invalid: %s%s", - err_msg, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->acquire_imsi = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_match_imsi, - cfg_gbproxy_no_match_imsi_cmd, - "no match-imsi", - NO_STR GBPROXY_MATCH_IMSI_STR) -{ - enum gbproxy_match_id match_id; - - for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) - gbproxy_clear_patch_filter(&g_cfg->matches[match_id]); - - g_cfg->acquire_imsi = false; - - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n" -#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n" - -static int set_core_apn(struct vty *vty, const char *apn) -{ - int apn_len; - - if (!apn) { - talloc_free(g_cfg->core_apn); - g_cfg->core_apn = NULL; - g_cfg->core_apn_size = 0; - return CMD_SUCCESS; - } - - apn_len = strlen(apn); - - if (apn_len >= 100) { - vty_out(vty, "APN string too long (max 99 chars)%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (apn_len == 0) { - talloc_free(g_cfg->core_apn); - /* TODO: replace NULL */ - g_cfg->core_apn = talloc_zero_size(NULL, 2); - g_cfg->core_apn_size = 0; - } else { - /* TODO: replace NULL */ - g_cfg->core_apn = - talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1); - g_cfg->core_apn_size = - gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_core_apn, - cfg_gbproxy_core_apn_cmd, - "core-access-point-name (APN|none)", - GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR) -{ - if (strcmp(argv[0], "none") == 0) - return set_core_apn(vty, ""); - else - return set_core_apn(vty, argv[0]); -} - -DEFUN(cfg_gbproxy_no_core_apn, - cfg_gbproxy_no_core_apn_cmd, - "no core-access-point-name", - NO_STR GBPROXY_CORE_APN_STR) -{ - return set_core_apn(vty, NULL); -} - -/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled - * automatically when needed. This command is only left for manual testing - * (e.g. doing P-TMSI patching without using a secondary SGSN) - */ -#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n" - -DEFUN(cfg_gbproxy_patch_ptmsi, - cfg_gbproxy_patch_ptmsi_cmd, - "patch-ptmsi", - GBPROXY_PATCH_PTMSI_STR) -{ - g_cfg->patch_ptmsi = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_patch_ptmsi, - cfg_gbproxy_no_patch_ptmsi_cmd, - "no patch-ptmsi", - NO_STR GBPROXY_PATCH_PTMSI_STR) -{ - g_cfg->patch_ptmsi = false; - - return CMD_SUCCESS; -} - -/* TODO: Remove the acquire-imsi command, since that feature is enabled - * automatically when IMSI matching is enabled. This command is only left for - * manual testing (e.g. doing IMSI acquisition without IMSI based patching) - */ -#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n" - -DEFUN(cfg_gbproxy_acquire_imsi, - cfg_gbproxy_acquire_imsi_cmd, - "acquire-imsi", - GBPROXY_ACQUIRE_IMSI_STR) -{ - g_cfg->acquire_imsi = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_acquire_imsi, - cfg_gbproxy_no_acquire_imsi_cmd, - "no acquire-imsi", - NO_STR GBPROXY_ACQUIRE_IMSI_STR) -{ - g_cfg->acquire_imsi = false; - - return CMD_SUCCESS; -} - -#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n" - -DEFUN(cfg_gbproxy_secondary_sgsn, - cfg_gbproxy_secondary_sgsn_cmd, - "secondary-sgsn nsei <0-65534>", - GBPROXY_SECOND_SGSN_STR - "NSEI to be used in the connection with the SGSN\n" - "The NSEI\n") -{ - unsigned int nsei = atoi(argv[0]); - - if (g_cfg->nsip_sgsn_nsei == nsei) { - vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->route_to_sgsn2 = true; - g_cfg->nsip_sgsn2_nsei = nsei; - - g_cfg->patch_ptmsi = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_secondary_sgsn, - cfg_gbproxy_no_secondary_sgsn_cmd, - "no secondary-sgsn", - NO_STR GBPROXY_SECOND_SGSN_STR) -{ - g_cfg->route_to_sgsn2 = false; - g_cfg->nsip_sgsn2_nsei = 0xFFFF; - - g_cfg->patch_ptmsi = false; - - return CMD_SUCCESS; -} - -#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n" -#define GBPROXY_LINK_STR "Set TLLI parameters\n" - -#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n" - -DEFUN(cfg_gbproxy_link_list_clean_stale_timer, - cfg_gbproxy_link_list_clean_stale_timer_cmd, - "link-list clean-stale-timer <1-999999>", - GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR - "Frequency at which the periodic timer is fired (in seconds)\n") -{ - struct gbproxy_peer *peer; - g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]); - - /* Re-schedule running timers soon in case prev frequency was really big - and new frequency is desired to be lower. After initial run, periodic - time is used. Use random() to avoid firing timers for all peers at - the same time */ - llist_for_each_entry(peer, &g_cfg->bts_peers, list) - osmo_timer_schedule(&peer->clean_stale_timer, - random() % 5, random() % 1000000); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer, - cfg_gbproxy_link_list_no_clean_stale_timer_cmd, - "no link-list clean-stale-timer", - NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR) - -{ - struct gbproxy_peer *peer; - g_cfg->clean_stale_timer_freq = 0; - - llist_for_each_entry(peer, &g_cfg->bts_peers, list) - osmo_timer_del(&peer->clean_stale_timer); - - return CMD_SUCCESS; -} - -#define GBPROXY_MAX_AGE_STR "Limit maximum age\n" - -DEFUN(cfg_gbproxy_link_list_max_age, - cfg_gbproxy_link_list_max_age_cmd, - "link-list max-age <1-999999>", - GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR - "Maximum age in seconds\n") -{ - g_cfg->tlli_max_age = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_no_max_age, - cfg_gbproxy_link_list_no_max_age_cmd, - "no link-list max-age", - NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR) -{ - g_cfg->tlli_max_age = 0; - - return CMD_SUCCESS; -} - -#define GBPROXY_MAX_LEN_STR "Limit list length\n" - -DEFUN(cfg_gbproxy_link_list_max_len, - cfg_gbproxy_link_list_max_len_cmd, - "link-list max-length <1-99999>", - GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR - "Maximum number of logical links in the list\n") -{ - g_cfg->tlli_max_len = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_no_max_len, - cfg_gbproxy_link_list_no_max_len_cmd, - "no link-list max-length", - NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR) -{ - g_cfg->tlli_max_len = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_keep_mode, - cfg_gbproxy_link_list_keep_mode_cmd, - "link-list keep-mode (never|re-attach|identified|always)", - GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n" - "Discard entry immediately after detachment\n" - "Keep entry if a re-attachment has be requested\n" - "Keep entry if it associated with an IMSI\n" - "Don't discard entries after detachment\n") -{ - int val = get_string_value(keep_modes, argv[0]); - OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS); - g_cfg->keep_link_infos = val; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_stored_msgs_max_len, - cfg_gbproxy_link_stored_msgs_max_len_cmd, - "link stored-msgs-max-length <1-99999>", - GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR - "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n") -{ - g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len, - cfg_gbproxy_link_no_stored_msgs_max_len_cmd, - "no link stored-msgs-max-length", - NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR) -{ - g_cfg->stored_msgs_max_len = 0; - - return CMD_SUCCESS; -} - - -DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]", - SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n") -{ - struct gbproxy_peer *peer; - int show_stats = argc >= 1; - - if (show_stats) - vty_out_rate_ctr_group(vty, "", g_cfg->ctrg); - - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - gbprox_vty_print_peer(vty, peer); - - if (show_stats) - vty_out_rate_ctr_group(vty, " ", peer->ctrg); - } - return CMD_SUCCESS; -} - -DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links", - SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n") -{ - struct gbproxy_peer *peer; - char mi_buf[200]; - time_t now; - struct timespec ts = {0,}; - - osmo_clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - gbprox_vty_print_peer(vty, peer); - - llist_for_each_entry(link_info, &state->logical_links, list) { - time_t age = now - link_info->timestamp; - - if (link_info->imsi > 0) { - snprintf(mi_buf, sizeof(mi_buf), "(invalid)"); - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - link_info->imsi, - link_info->imsi_len); - } else { - snprintf(mi_buf, sizeof(mi_buf), "(none)"); - } - vty_out(vty, " TLLI %08x, IMSI %s, AGE %d", - link_info->tlli.current, mi_buf, (int)age); - - if (link_info->stored_msgs_len) - vty_out(vty, ", STORED %"PRIu32"/%"PRIu32, - link_info->stored_msgs_len, - g_cfg->stored_msgs_max_len); - - if (g_cfg->route_to_sgsn2) - vty_out(vty, ", SGSN NSEI %d", - link_info->sgsn_nsei); - - if (link_info->is_deregistered) - vty_out(vty, ", DE-REGISTERED"); - - vty_out(vty, "%s", VTY_NEWLINE); - } - } - return CMD_SUCCESS; -} - -DEFUN(delete_gb_bvci, delete_gb_bvci_cmd, - "delete-gbproxy-peer <0-65534> bvci <2-65534>", - "Delete a GBProxy peer by NSEI and optionally BVCI\n" - "NSEI number\n" - "Only delete peer with a matching BVCI\n" - "BVCI number\n") -{ - const uint16_t nsei = atoi(argv[0]); - const uint16_t bvci = atoi(argv[1]); - int counter; - - counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci); - - if (counter == 0) { - vty_out(vty, "BVC not found%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(delete_gb_nsei, delete_gb_nsei_cmd, - "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]", - "Delete a GBProxy peer by NSEI and optionally BVCI\n" - "NSEI number\n" - "Only delete BSSGP connections (BVC)\n" - "Only delete dynamic NS connections (NS-VC)\n" - "Delete BVC and dynamic NS connections\n" - "Show what would be deleted instead of actually deleting\n" - ) -{ - const uint16_t nsei = atoi(argv[0]); - const char *mode = argv[1]; - int dry_run = argc > 2; - int delete_bvc = 0; - int delete_nsvc = 0; - int counter; - - if (strcmp(mode, "only-bvc") == 0) - delete_bvc = 1; - else if (strcmp(mode, "only-nsvc") == 0) - delete_nsvc = 1; - else - delete_bvc = delete_nsvc = 1; - - if (delete_bvc) { - if (!dry_run) - counter = gbproxy_cleanup_peers(g_cfg, nsei, 0); - else { - struct gbproxy_peer *peer; - counter = 0; - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - if (peer->nsei != nsei) - continue; - - vty_out(vty, "BVC: "); - gbprox_vty_print_peer(vty, peer); - counter += 1; - } - } - vty_out(vty, "%sDeleted %d BVC%s", - dry_run ? "Not " : "", counter, VTY_NEWLINE); - } - - if (delete_nsvc) { - struct gprs_ns_inst *nsi = g_cfg->nsi; - struct gprs_nsvc *nsvc, *nsvc2; - - counter = 0; - llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) { - if (nsvc->nsei != nsei) - continue; - if (nsvc->persistent) - continue; - - if (!dry_run) - gprs_nsvc_delete(nsvc); - else - vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, " - "remote %s%s", - nsvc->nsei, nsvc->nsvci, - gprs_ns_ll_str(nsvc), VTY_NEWLINE); - counter += 1; - } - vty_out(vty, "%sDeleted %d NS-VC%s", - dry_run ? "Not " : "", counter, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -#define GBPROXY_DELETE_LINK_STR \ - "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n" - -DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd, - "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT", - GBPROXY_DELETE_LINK_STR - "Delete entries with a matching TLLI (hex)\n" - "Delete entries with a matching IMSI\n" - "Delete entries with a matching SGSN NSEI\n" - "Identification to match\n") -{ - const uint16_t nsei = atoi(argv[0]); - enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match; - uint32_t ident = 0; - const char *imsi = NULL; - struct gbproxy_peer *peer = 0; - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state; - char mi_buf[200]; - int found = 0; - - match = argv[1][0]; - - switch (match) { - case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break; - case MATCH_IMSI: imsi = argv[2]; break; - case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break; - }; - - peer = gbproxy_peer_by_nsei(g_cfg, nsei); - if (!peer) { - vty_out(vty, "Didn't find peer with NSEI %d%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - state = &peer->patch_state; - - llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) { - switch (match) { - case MATCH_TLLI: - if (link_info->tlli.current != ident) - continue; - break; - case MATCH_SGSN: - if (link_info->sgsn_nsei != ident) - continue; - break; - case MATCH_IMSI: - if (!link_info->imsi) - continue; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - link_info->imsi, - link_info->imsi_len); - - if (strcmp(mi_buf, imsi) != 0) - continue; - break; - } - - vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current, - VTY_NEWLINE); - gbproxy_delete_link_info(peer, link_info); - found += 1; - } - - if (!found && argc >= 2) { - vty_out(vty, "Didn't find link entry with %s %s%s", - argv[1], argv[2], VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(delete_gb_link, delete_gb_link_cmd, - "delete-gbproxy-link <0-65534> (stale|de-registered)", - GBPROXY_DELETE_LINK_STR - "Delete stale entries\n" - "Delete de-registered entries\n") -{ - const uint16_t nsei = atoi(argv[0]); - enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match; - struct gbproxy_peer *peer = 0; - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state; - time_t now; - struct timespec ts = {0,}; - - int found = 0; - - match = argv[1][0]; - - peer = gbproxy_peer_by_nsei(g_cfg, nsei); - if (!peer) { - vty_out(vty, "Didn't find peer with NSEI %d%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - state = &peer->patch_state; - - osmo_clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - if (match == MATCH_STALE) { - found = gbproxy_remove_stale_link_infos(peer, now); - if (found) - vty_out(vty, "Deleted %d stale logical link%s%s", - found, found == 1 ? "" : "s", VTY_NEWLINE); - } else { - llist_for_each_entry_safe(link_info, nxt, - &state->logical_links, list) { - if (!link_info->is_deregistered) - continue; - - gbproxy_delete_link_info(peer, link_info); - found += 1; - } - } - - if (found) - vty_out(vty, "Deleted %d %s logical link%s%s", - found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -/* - * legacy commands to provide an upgrade path from "broken" releases - * or pre-releases - */ -DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match, - cfg_gbproxy_broken_apn_match_cmd, - "core-access-point-name none match-imsi .REGEXP", - GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n" - "Patch MS related information elements on match only\n" - "Route to the secondary SGSN on match only\n" - "Regular expression for the IMSI match\n") -{ - const char *filter = argv[0]; - const char *err_msg = NULL; - struct gbproxy_match *match; - enum gbproxy_match_id match_id = get_string_value(match_ids, "patching"); - - /* apply APN none */ - set_core_apn(vty, ""); - - /* do the matching... with copy and paste */ - OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && - match_id < GBPROX_MATCH_LAST); - match = &g_cfg->matches[match_id]; - - if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { - vty_out(vty, "Match expression invalid: %s%s", - err_msg, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->acquire_imsi = true; - - return CMD_SUCCESS; -} - -#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n" -#define GBPROXY_MAX_LEN_STR "Limit list length\n" -DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len, - cfg_gbproxy_depr_tlli_list_max_len_cmd, - "tlli-list max-length <1-99999>", - GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR - "Maximum number of TLLIs in the list\n") -{ - g_cfg->tlli_max_len = atoi(argv[0]); - - return CMD_SUCCESS; -} - -int gbproxy_vty_init(void) -{ - install_element_ve(&show_gbproxy_cmd); - install_element_ve(&show_gbproxy_links_cmd); - - install_element(ENABLE_NODE, &delete_gb_bvci_cmd); - install_element(ENABLE_NODE, &delete_gb_nsei_cmd); - install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd); - install_element(ENABLE_NODE, &delete_gb_link_cmd); - - install_element(CONFIG_NODE, &cfg_gbproxy_cmd); - install_node(&gbproxy_node, config_write_gbproxy); - install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd); - - /* broken or deprecated to allow an upgrade path */ - install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd); - - return 0; -} - -int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg) -{ - int rc; - - g_cfg = cfg; - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - return 0; -} diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am index 170ded8fc..1533dc070 100644 --- a/src/gprs/Makefile.am +++ b/src/gprs/Makefile.am @@ -29,7 +29,6 @@ endif noinst_LTLIBRARIES = libcommon.la libcommon_la_SOURCES = \ - gprs_gb_parse.c \ gprs_llc_parse.c \ crc24.c \ gprs_utils.c \ diff --git a/src/gprs/crc24.c b/src/gprs/crc24.c index da269b375..d47d4ff82 100644 --- a/src/gprs/crc24.c +++ b/src/gprs/crc24.c @@ -59,7 +59,7 @@ static const uint32_t tbl_crc24[256] = { #define INIT_CRC24 0xffffff -uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len) +uint32_t crc24_calc(uint32_t fcs, const uint8_t *cp, unsigned int len) { while (len--) fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff]; diff --git a/src/gprs/gprs_gb_parse.c b/src/gprs/gprs_gb_parse.c deleted file mode 100644 index 379674a91..000000000 --- a/src/gprs/gprs_gb_parse.c +++ /dev/null @@ -1,646 +0,0 @@ -/* GPRS Gb message parser */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/gsm/gsm48.h> -#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> - -#include <osmocom/sgsn/gprs_gb_parse.h> - -#include <osmocom/sgsn/gprs_utils.h> - -#include <osmocom/sgsn/debug.h> - -#include <osmocom/gprs/gprs_bssgp.h> - -static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_REQ"; - - /* Skip MS network capability */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 1 || value_len > 8) - /* invalid */ - return 0; - - /* Skip Attach type */ - /* Skip Ciphering key sequence number */ - /* Skip DRX parameter */ - if (osmo_shift_v_fixed(&data, &data_len, 3, NULL) < 3) - return 0; - - /* Get Mobile identity */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || - value_len < 5 || value_len > 8) - /* invalid */ - return 0; - - if (gprs_is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value + 1; - } else if (gprs_is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_ACK"; - - /* Skip Attach result */ - /* Skip Force to standby */ - /* Skip Periodic RA update timer */ - /* Skip Radio priority for SMS */ - /* Skip Spare half octet */ - if (osmo_shift_v_fixed(&data, &data_len, 3, NULL) < 3) - return 0; - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - return 1; -} - -static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - - parse_ctx->llc_msg_name = "ATTACH_REJ"; - - /* GMM cause */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - parse_ctx->invalidate_tlli = 1; - - return 1; -} - - -static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - int detach_type; - int power_off; - - parse_ctx->llc_msg_name = "DETACH_REQ"; - - /* Skip spare half octet */ - /* Get Detach type */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - /* invalid */ - return 0; - - detach_type = *value & 0x07; - power_off = *value & 0x08 ? 1 : 0; - - if (parse_ctx->to_bss) { - /* Network originated */ - if (detach_type == GPRS_DET_T_MT_REATT_REQ) - parse_ctx->await_reattach = 1; - } else { - /* Mobile originated */ - - if (power_off) - parse_ctx->invalidate_tlli = 1; - - /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */ - if (osmo_match_shift_tlv(&data, &data_len, - GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0) - { - if (gprs_is_mi_tmsi(value, value_len)) - parse_ctx->ptmsi_enc = value + 1; - } - } - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - - parse_ctx->llc_msg_name = "RA_UPD_REQ"; - - /* Skip Update type */ - /* Skip GPRS ciphering key sequence number */ - if (osmo_shift_v_fixed(&data, &data_len, 1, NULL) < 1) - return 0; - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - uint8_t cause; - int force_standby; - - parse_ctx->llc_msg_name = "RA_UPD_REJ"; - - /* GMM cause */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - cause = value[0]; - - /* Force to standby, 1/2 */ - /* spare bits, 1/2 */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - force_standby = (value[0] & 0x07) == 0x01; - - if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby) - parse_ctx->await_reattach = 1; - - parse_ctx->invalidate_tlli = 1; - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "RA_UPD_ACK"; - - /* Skip Force to standby */ - /* Skip Update result */ - /* Skip Periodic RA update timer */ - if (osmo_shift_v_fixed(&data, &data_len, 2, NULL) < 2) - return 0; - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - - return 1; -} - -static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "PTMSI_REALL_CMD"; - - LOGP(DLLC, LOGL_NOTICE, - "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n"); - - /* Allocated P-TMSI */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ID_RESP"; - - /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || - value_len < 1 || value_len > 9) - /* invalid */ - return 0; - - if (gprs_is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value + 1; - } else if (gprs_is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - return 1; -} - -static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - ssize_t old_len; - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ACT_PDP_REQ"; - - /* Skip Requested NSAPI */ - /* Skip Requested LLC SAPI */ - if (osmo_shift_v_fixed(&data, &data_len, 2, NULL) < 2) - return 0; - - /* Skip Requested QoS (support 04.08 and 24.008) */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 4 || value_len > 14) - /* invalid */ - return 0; - - /* Skip Requested PDP address */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 2 || value_len > 18) - /* invalid */ - return 0; - - /* Access point name */ - old_len = osmo_match_shift_tlv(&data, &data_len, - GSM48_IE_GSM_APN, &value, &value_len); - - if (old_len > 0 && value_len >=1 && value_len <= 100) { - parse_ctx->apn_ie = data - old_len; - parse_ctx->apn_ie_len = old_len; - } - - return 1; -} - -int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gsm48_hdr *g48h; - uint8_t pdisc; - uint8_t msg_type; - - if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) - return 0; - - parse_ctx->g48_hdr = g48h; - - pdisc = gsm48_hdr_pdisc(g48h); - if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS) - return 1; - - msg_type = gsm48_hdr_msg_type(g48h); - switch (msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ATTACH_REJ: - return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ATTACH_ACK: - return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_REQ: - return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_REJ: - return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_ACK: - return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_PTMSI_REALL_CMD: - return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx); - - case GSM48_MT_GSM_ACT_PDP_REQ: - return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ID_RESP: - return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_REQ: - return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_ACK: - parse_ctx->llc_msg_name = "DETACH_ACK"; - parse_ctx->invalidate_tlli = 1; - break; - - case GSM48_MT_GSM_DEACT_PDP_REQ: - parse_ctx->llc_msg_name = "DEACT_PDP_REQ"; - break; - - case GSM48_MT_GSM_DEACT_PDP_ACK: - parse_ctx->llc_msg_name = "DEACT_PDP_ACK"; - break; - - default: - LOGP(DLLC, LOGL_NOTICE, - "Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n", - get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc)); - break; - }; - - return 1; -} - -int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int rc; - int fcs; - - /* parse LLC */ - rc = gprs_llc_hdr_parse(ghp, llc, llc_len); - gprs_llc_hdr_dump(ghp, NULL); - if (rc != 0) { - LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return 0; - } - - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", - ghp->fcs, fcs); - - if (!ghp->data) - return 0; - - if (ghp->sapi != GPRS_SAPI_GMM) - return 1; - - if (ghp->cmd != GPRS_LLC_UI) - return 1; - - if (ghp->is_encrypted) { - parse_ctx->need_decryption = 1; - return 0; - } - - return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx); -} - -int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct bssgp_normal_hdr *bgph; - struct bssgp_ud_hdr *budh = NULL; - struct tlv_parsed *tp = &parse_ctx->bssgp_tp; - uint8_t pdu_type; - uint8_t *data; - size_t data_len; - int rc; - - if (bssgp_len < sizeof(struct bssgp_normal_hdr)) - return 0; - - bgph = (struct bssgp_normal_hdr *)bssgp; - pdu_type = bgph->pdu_type; - - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - if (bssgp_len < sizeof(struct bssgp_ud_hdr)) - return 0; - budh = (struct bssgp_ud_hdr *)bssgp; - bgph = NULL; - data = budh->data; - data_len = bssgp_len - sizeof(*budh); - } else { - data = bgph->data; - data_len = bssgp_len - sizeof(*bgph); - } - - parse_ctx->pdu_type = pdu_type; - parse_ctx->bud_hdr = budh; - parse_ctx->bgp_hdr = bgph; - parse_ctx->bssgp_data = data; - parse_ctx->bssgp_data_len = data_len; - - if (bssgp_tlv_parse(tp, data, data_len) < 0) - return 0; - - if (budh) - parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - - if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); - - if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { - parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); - parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) { - if (parse_ctx->tlli_enc) - /* This is TLLI old, don't confuse it with TLLI current */ - parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); - else - parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS) - parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI); - - if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { - uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); - size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); - - rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx); - if (!rc) - return 0; - - parse_ctx->llc = llc; - parse_ctx->llc_len = llc_len; - } - - if (parse_ctx->tlli_enc) { - uint32_t tmp_tlli; - memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); - parse_ctx->tlli = ntohl(tmp_tlli); - } - - if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc && - memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0) - parse_ctx->old_raid_is_foreign = 1; - - return 1; -} - -void gprs_gb_log_parse_context(int log_level, - struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name) -{ - const char *msg_name; - const char *sep = ""; - - if (!parse_ctx->tlli_enc && - !parse_ctx->ptmsi_enc && - !parse_ctx->new_ptmsi_enc && - !parse_ctx->bssgp_ptmsi_enc && - !parse_ctx->imsi) - return; - - msg_name = gprs_gb_message_name(parse_ctx, default_msg_name); - - if (parse_ctx->llc_msg_name) - msg_name = parse_ctx->llc_msg_name; - - LOGP(DGPRS, log_level, "%s: Got", msg_name); - - if (parse_ctx->tlli_enc) { - LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli); - sep = ","; - } - - if (parse_ctx->old_tlli_enc) { - LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep, - parse_ctx->old_tlli_enc[0], - parse_ctx->old_tlli_enc[1], - parse_ctx->old_tlli_enc[2], - parse_ctx->old_tlli_enc[3]); - sep = ","; - } - - if (parse_ctx->bssgp_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc); - LOGPC(DGPRS, log_level, "%s BSSGP RAID %s", sep, osmo_rai_name(&raid)); - sep = ","; - } - - if (parse_ctx->raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->raid_enc); - LOGPC(DGPRS, log_level, "%s RAID %s", sep, osmo_rai_name(&raid)); - sep = ","; - } - - if (parse_ctx->old_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->old_raid_enc); - LOGPC(DGPRS, log_level, "%s old RAID %s", sep, osmo_rai_name(&raid)); - sep = ","; - } - - if (parse_ctx->bssgp_ptmsi_enc) { - uint32_t ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi); - LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi); - sep = ","; - } - - if (parse_ctx->ptmsi_enc) { - uint32_t ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi); - LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi); - sep = ","; - } - - if (parse_ctx->new_ptmsi_enc) { - uint32_t new_ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi); - sep = ","; - } - - if (parse_ctx->imsi) { - char mi_buf[200]; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - parse_ctx->imsi, parse_ctx->imsi_len); - LOGPC(DGPRS, log_level, "%s IMSI %s", - sep, mi_buf); - sep = ","; - } - if (parse_ctx->invalidate_tlli) { - LOGPC(DGPRS, log_level, "%s invalidate", sep); - sep = ","; - } - if (parse_ctx->await_reattach) { - LOGPC(DGPRS, log_level, "%s re-attach", sep); - sep = ","; - } - - LOGPC(DGPRS, log_level, "\n"); -} - -const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name) -{ - if (parse_ctx->llc_msg_name) - return parse_ctx->llc_msg_name; - - if (parse_ctx->g48_hdr) - return "GMM"; - - if (parse_ctx->llc) - return "LLC"; - - if (parse_ctx->bud_hdr) - return "BSSGP-UNITDATA"; - - if (parse_ctx->bgp_hdr) - return "BSSGP"; - - return "unknown"; -} diff --git a/src/gprs/gprs_llc_parse.c b/src/gprs/gprs_llc_parse.c index 1d97004e0..d099cdaf3 100644 --- a/src/gprs/gprs_llc_parse.c +++ b/src/gprs/gprs_llc_parse.c @@ -29,7 +29,7 @@ #include <osmocom/gprs/gprs_bssgp.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_llc.h> #include <osmocom/sgsn/crc24.h> @@ -55,7 +55,7 @@ static const struct value_string llc_cmd_strs[] = { #define N202 4 #define CRC24_LENGTH 3 -int gprs_llc_fcs(uint8_t *data, unsigned int len) +int gprs_llc_fcs(const uint8_t *data, unsigned int len) { uint32_t fcs_calc; diff --git a/src/gprs/gprs_utils.c b/src/gprs/gprs_utils.c index 13641c1e1..46028c63c 100644 --- a/src/gprs/gprs_utils.c +++ b/src/gprs/gprs_utils.c @@ -22,7 +22,7 @@ #include <osmocom/sgsn/gprs_utils.h> #include <osmocom/core/msgb.h> -#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_ns2.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/gsm/protocol/gsm_04_08.h> @@ -30,56 +30,6 @@ #include <string.h> -int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str) -{ - uint8_t *last_len_field; - int len; - - /* Can we even write the length field to the output? */ - if (max_len == 0) - return -1; - - /* Remember where we need to put the length once we know it */ - last_len_field = apn_enc; - len = 1; - apn_enc += 1; - - while (str[0]) { - if (len >= max_len) - return -1; - - if (str[0] == '.') { - *last_len_field = (apn_enc - last_len_field) - 1; - last_len_field = apn_enc; - } else { - *apn_enc = str[0]; - } - apn_enc += 1; - str += 1; - len += 1; - } - - *last_len_field = (apn_enc - last_len_field) - 1; - - return len; -} - -/* GSM 04.08, 10.5.7.3 GPRS Timer */ -int gprs_tmr_to_secs(uint8_t tmr) -{ - switch (tmr & GPRS_TMR_UNIT_MASK) { - case GPRS_TMR_2SECONDS: - return 2 * (tmr & GPRS_TMR_FACT_MASK); - default: - case GPRS_TMR_MINUTE: - return 60 * (tmr & GPRS_TMR_FACT_MASK); - case GPRS_TMR_6MINUTE: - return 360 * (tmr & GPRS_TMR_FACT_MASK); - case GPRS_TMR_DEACTIVATED: - return -1; - } -} - /* This functions returns a tmr value such that * - f is monotonic * - f(s) <= s @@ -132,19 +82,6 @@ int gprs_is_mi_imsi(const uint8_t *value, size_t value_len) return 1; } -int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) -{ - uint32_t tmsi_be; - - if (!gprs_is_mi_tmsi(value, value_len)) - return 0; - - memcpy(&tmsi_be, value + 1, sizeof(tmsi_be)); - - *tmsi = ntohl(tmsi_be); - return 1; -} - void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi) { uint32_t tmsi_be; diff --git a/src/gprs/sgsn_ares.c b/src/gprs/sgsn_ares.c index ba168d62e..81ab83540 100644 --- a/src/gprs/sgsn_ares.c +++ b/src/gprs/sgsn_ares.c @@ -24,8 +24,6 @@ #include <netdb.h> -extern void *tall_sgsn_ctx; - struct cares_event_fd { struct llist_head head; struct osmo_fd fd; @@ -51,8 +49,8 @@ static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what) LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what); ares_process_fd(sgsn->ares_channel, - (what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD, - (what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD); + (what & OSMO_FD_READ) ? fd->fd : ARES_SOCKET_BAD, + (what & OSMO_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD); osmo_ares_reschedule(sgsn); return 0; } @@ -120,14 +118,14 @@ static void setup_ares_osmo_fd(void *data, int fd, int read, int write) update_fd: if (read) - ufd->fd.when |= BSC_FD_READ; + osmo_fd_read_enable(&ufd->fd); else - ufd->fd.when &= ~BSC_FD_READ; + osmo_fd_read_disable(&ufd->fd); if (write) - ufd->fd.when |= BSC_FD_WRITE; + osmo_fd_write_enable(&ufd->fd); else - ufd->fd.when &= ~BSC_FD_WRITE; + osmo_fd_write_disable(&ufd->fd); osmo_ares_reschedule(sgsn); } diff --git a/src/gtphub/gtphub.c b/src/gtphub/gtphub.c index ca5857b39..937e6a4ff 100644 --- a/src/gtphub/gtphub.c +++ b/src/gtphub/gtphub.c @@ -32,7 +32,7 @@ #include <gtp.h> #include <gtpie.h> -#include <osmocom/sgsn/gtphub.h> +#include <osmocom/gtphub/gtphub.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/gprs_utils.h> @@ -59,15 +59,6 @@ void *osmo_gtphub_ctx; /* TODO move this to osmocom/core/select.h ? */ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); -/* TODO move this to osmocom/core/linuxlist.h ? */ -#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) -#define llist_first(head, type, entry) \ - llist_entry(__llist_first(head), type, entry) - -#define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev) -#define llist_last(head, type, entry) \ - llist_entry(__llist_last(head), type, entry) - /* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */ enum gtp_rc { @@ -167,12 +158,12 @@ void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src) } int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, - const struct osmo_sockaddr *sa) + const struct sgsn_sockaddr *sa) { char addr_str[256]; char port_str[6]; - if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str), + if (sgsn_sockaddr_to_strs(addr_str, sizeof(addr_str), port_str, sizeof(port_str), sa, (NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { @@ -484,7 +475,7 @@ static int get_ie_imsi_str(union gtpie_member *ie[], int i, * present but cannot be decoded. */ static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str) { - static char apn_buf[GSM_APN_LENGTH]; + static char apn_buf[APN_MAXLEN+1]; unsigned int len; if (gtpie_gettlv(ie, GTPIE_APN, 0, &len, apn_buf, sizeof(apn_buf)) != 0) @@ -613,7 +604,7 @@ void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now) OSMO_ASSERT(llist_empty(&exq->items) || (item->expiry - >= llist_last(&exq->items, struct expiring_item, entry)->expiry)); + >= llist_last_entry(&exq->items, struct expiring_item, entry)->expiry)); /* Add/move to the tail to always sort by expiry, ascending. */ llist_del(&item->entry); @@ -853,10 +844,7 @@ static int gtphub_sock_init(struct osmo_fd *ofd, return -1; } - ofd->when = BSC_FD_READ; - ofd->cb = cb; - ofd->data = data; - ofd->priv_nr = ofd_id; + osmo_fd_setup(ofd, -1, OSMO_FD_READ, cb, data, ofd_id); int rc; rc = osmo_sock_init_ofd(ofd, @@ -925,7 +913,7 @@ static void gtphub_bind_stop(struct gtphub_bind *b) { /* Recv datagram from from->fd, write sender's address to *from_addr. * Return the number of bytes read, zero on error. */ static int gtphub_read(const struct osmo_fd *from, - struct osmo_sockaddr *from_addr, + struct sgsn_sockaddr *from_addr, uint8_t *buf, size_t buf_len) { OSMO_ASSERT(from_addr); @@ -946,7 +934,7 @@ static int gtphub_read(const struct osmo_fd *from, } LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n", - (int)received, osmo_sockaddr_to_str(from_addr), + (int)received, sgsn_sockaddr_to_str(from_addr), osmo_hexdump(buf, received > 1000? 1000 : received), received > 1000 ? "..." : ""); @@ -983,7 +971,7 @@ static inline void set_tei(struct gtp_packet_desc *p, uint32_t tei) static void gtphub_mapping_del_cb(struct expiring_item *expi); -static struct nr_mapping *gtphub_mapping_new() +static struct nr_mapping *gtphub_mapping_new(void) { struct nr_mapping *nrm; nrm = talloc_zero(osmo_gtphub_ctx, struct nr_mapping); @@ -1007,7 +995,7 @@ static const char *gtphub_tunnel_side_str(struct gtphub_tunnel *tun, char *pos = buf; int left = sizeof(buf); int l; - + struct gtphub_tunnel_endpoint *c, *u; c = &tun->endpoint[side_idx][GTPH_PLANE_CTRL]; u = &tun->endpoint[side_idx][GTPH_PLANE_USER]; @@ -1116,7 +1104,7 @@ static void gtphub_tunnel_del_cb(struct expiring_item *expi) /* rate counter index for hubs: [7; 10] */ #define CTR_IDX_HUB(s, p) CTR_IDX(s, p, 3, 2) -static struct gtphub_tunnel *gtphub_tunnel_new() +static struct gtphub_tunnel *gtphub_tunnel_new(void) { struct gtphub_tunnel *tun; tun = talloc_zero(osmo_gtphub_ctx, struct gtphub_tunnel); @@ -1145,9 +1133,7 @@ static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf, if (llist_empty(&peer->addresses)) return "(addressless)"; - struct gtphub_peer_addr *a = llist_first(&peer->addresses, - struct gtphub_peer_addr, - entry); + struct gtphub_peer_addr *a = llist_first_entry_or_null(&peer->addresses, struct gtphub_peer_addr, entry); return gsn_addr_to_strb(&a->addr, buf, buflen); } @@ -1948,14 +1934,14 @@ static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n", gtphub_plane_idx_names[plane_idx]); - if (!(what & BSC_FD_READ)) + if (!(what & OSMO_FD_READ)) return 0; struct gtphub *hub = from_sgsns_ofd->data; static uint8_t buf[4096]; - struct osmo_sockaddr from_addr; - struct osmo_sockaddr to_addr; + struct sgsn_sockaddr from_addr; + struct sgsn_sockaddr to_addr; struct osmo_fd *to_ofd; int len; uint8_t *reply_buf; @@ -1979,14 +1965,14 @@ static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) OSMO_ASSERT(plane_idx < GTPH_PLANE_N); LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n", gtphub_plane_idx_names[plane_idx]); - if (!(what & BSC_FD_READ)) + if (!(what & OSMO_FD_READ)) return 0; struct gtphub *hub = from_ggsns_ofd->data; static uint8_t buf[4096]; - struct osmo_sockaddr from_addr; - struct osmo_sockaddr to_addr; + struct sgsn_sockaddr from_addr; + struct sgsn_sockaddr to_addr; struct osmo_fd *to_ofd; int len; uint8_t *reply_buf; @@ -2071,9 +2057,9 @@ static int gtphub_unmap(struct gtphub *hub, static int gsn_addr_to_sockaddr(struct gsn_addr *src, uint16_t port, - struct osmo_sockaddr *dst) + struct sgsn_sockaddr *dst) { - return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port); + return sgsn_sockaddr_init_udp(dst, gsn_addr_to_str(src), port); } /* If p is an Echo request, replace p's data with the matching response and @@ -2107,7 +2093,7 @@ static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p, } struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr); + const struct sgsn_sockaddr *addr); /* Parse buffer as GTP packet, replace elements in-place and return the ofd and * address to forward to. Return a pointer to the osmo_fd, but copy the @@ -2117,18 +2103,18 @@ struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *b int gtphub_handle_buf(struct gtphub *hub, unsigned int side_idx, unsigned int plane_idx, - const struct osmo_sockaddr *from_addr, + const struct sgsn_sockaddr *from_addr, uint8_t *buf, size_t received, time_t now, uint8_t **reply_buf, struct osmo_fd **to_ofd, - struct osmo_sockaddr *to_addr) + struct sgsn_sockaddr *to_addr) { struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx]; struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx]; - rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_IN], + rate_ctr_add(rate_ctr_group_get_ctr(from_bind->counters_io, GTPH_CTR_BYTES_IN), received); struct gtp_packet_desc p; @@ -2138,7 +2124,7 @@ int gtphub_handle_buf(struct gtphub *hub, (side_idx == GTPH_SIDE_GGSN)? "<-" : "->", gtphub_plane_idx_names[plane_idx], gtphub_side_idx_names[side_idx], - osmo_sockaddr_to_str(from_addr), + sgsn_sockaddr_to_str(from_addr), gtp_type_str(p.type)); if (p.rc <= 0) { @@ -2146,26 +2132,26 @@ int gtphub_handle_buf(struct gtphub *hub, gtp_type_str(p.type), gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], - osmo_sockaddr_to_str(from_addr)); + sgsn_sockaddr_to_str(from_addr)); return -1; } - rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_IN]); + rate_ctr_inc(rate_ctr_group_get_ctr(from_bind->counters_io, GTPH_CTR_PKTS_IN)); int reply_len; reply_len = gtphub_handle_echo_req(hub, &p, reply_buf); if (reply_len > 0) { /* It was an echo. Nothing left to do. */ - osmo_sockaddr_copy(to_addr, from_addr); + sgsn_sockaddr_copy(to_addr, from_addr); *to_ofd = &from_bind->ofd; - rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], + rate_ctr_inc(rate_ctr_group_get_ctr(from_bind->counters_io, GTPH_CTR_PKTS_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(from_bind->counters_io, GTPH_CTR_BYTES_OUT), reply_len); LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n", (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--", gtphub_side_idx_names[side_idx], - (int)reply_len, osmo_sockaddr_to_str(to_addr)); + (int)reply_len, sgsn_sockaddr_to_str(to_addr)); return reply_len; } if (reply_len < 0) @@ -2178,7 +2164,7 @@ int gtphub_handle_buf(struct gtphub *hub, * so no-one else is allowed to talk to us from that side. */ struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx]; if (from_peer) { - if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) { + if (sgsn_sockaddr_cmp(&from_peer->sa, from_addr) != 0) { LOG(LOGL_ERROR, "Rejecting: %s proxy configured, but GTP packet" " received on %s bind is from another sender:" @@ -2186,7 +2172,7 @@ int gtphub_handle_buf(struct gtphub *hub, gtphub_side_idx_names[side_idx], gtphub_side_idx_names[side_idx], gtphub_port_str(from_peer), - osmo_sockaddr_to_str(from_addr)); + sgsn_sockaddr_to_str(from_addr)); return -1; } } @@ -2204,7 +2190,7 @@ int gtphub_handle_buf(struct gtphub *hub, if (side_idx == GTPH_SIDE_GGSN) { LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n", gtp_type_str(p.type), - osmo_sockaddr_to_str(from_addr)); + sgsn_sockaddr_to_str(from_addr)); return -1; } else { /* SGSN */ @@ -2216,7 +2202,7 @@ int gtphub_handle_buf(struct gtphub *hub, "Dropping packet%s: User plane peer was not" "announced by PDP Context: %s\n", gtp_type_str(p.type), - osmo_sockaddr_to_str(from_addr)); + sgsn_sockaddr_to_str(from_addr)); return -1; } @@ -2235,13 +2221,13 @@ int gtphub_handle_buf(struct gtphub *hub, LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n", gtp_type_str(p.type), gtphub_side_idx_names[side_idx], - osmo_sockaddr_to_str(from_addr)); + sgsn_sockaddr_to_str(from_addr)); return -1; } - rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN], + rate_ctr_add(rate_ctr_group_get_ctr(from_peer->counters_io, GTPH_CTR_BYTES_IN), received); - rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]); + rate_ctr_inc(rate_ctr_group_get_ctr(from_peer->counters_io, GTPH_CTR_PKTS_IN)); LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx], gtphub_port_str(from_peer)); @@ -2260,9 +2246,9 @@ int gtphub_handle_buf(struct gtphub *hub, if (p.tun) { struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx]; - rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN], + rate_ctr_add(rate_ctr_group_get_ctr(te->counters_io, GTPH_CTR_BYTES_IN), received); - rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]); + rate_ctr_inc(rate_ctr_group_get_ctr(te->counters_io, GTPH_CTR_PKTS_IN)); } if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) { @@ -2297,7 +2283,7 @@ int gtphub_handle_buf(struct gtphub *hub, != 0) return -1; } - + /* Either to_peer was resolved from an existing tunnel, * or a PDP Ctx and thus a tunnel has just been created, * or the tunnel has been deleted due to this message. */ @@ -2309,24 +2295,24 @@ int gtphub_handle_buf(struct gtphub *hub, if (!to_peer_from_seq) gtphub_map_seq(&p, from_peer, to_peer); - osmo_sockaddr_copy(to_addr, &to_peer->sa); + sgsn_sockaddr_copy(to_addr, &to_peer->sa); *reply_buf = (uint8_t*)p.data; if (received) { - rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], + rate_ctr_inc(rate_ctr_group_get_ctr(to_bind->counters_io, GTPH_CTR_PKTS_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(to_bind->counters_io, GTPH_CTR_BYTES_OUT), received); - rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT], + rate_ctr_inc(rate_ctr_group_get_ctr(to_peer->counters_io, GTPH_CTR_PKTS_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(to_peer->counters_io, GTPH_CTR_BYTES_OUT), received); } if (p.tun) { struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx]; - rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT], + rate_ctr_inc(rate_ctr_group_get_ctr(te->counters_io, GTPH_CTR_PKTS_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(te->counters_io, GTPH_CTR_BYTES_OUT), received); } @@ -2335,7 +2321,7 @@ int gtphub_handle_buf(struct gtphub *hub, (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--", gtphub_side_idx_names[other_side_idx(side_idx)], p.header_tei, p.seq, - (int)received, osmo_sockaddr_to_str(to_addr)); + (int)received, sgsn_sockaddr_to_str(to_addr)); return received; } @@ -2645,7 +2631,7 @@ static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind, } struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr) + const struct sgsn_sockaddr *addr) { struct gsn_addr gsna; uint16_t port; @@ -2704,7 +2690,7 @@ static struct gtphub_peer_addr *gtphub_addr_have(struct gtphub *hub, struct gtphub_peer *peer = gtphub_peer_new(hub, bind); a = gtphub_peer_add_addr(peer, addr); - + LOG(LOGL_DEBUG, "New peer address: %s %s\n", bind->label, gsn_addr_to_str(&a->addr)); @@ -2762,7 +2748,7 @@ struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, /* Find a GGSN peer with a matching address. If the address is known but the * port not, create a new port for that peer address. */ struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr) + const struct sgsn_sockaddr *addr) { struct gtphub_peer_addr *pa; struct gtphub_peer_port *pp; @@ -2817,7 +2803,7 @@ static int gtphub_resolve_ggsn(struct gtphub *hub, /* TODO move to osmocom/core/socket.c ? */ /* use this in osmo_sock_init() to remove dup. */ -/* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required +/* Internal: call getaddrinfo for sgsn_sockaddr_init(). The caller is required to call freeaddrinfo(*result), iff zero is returned. */ static int _osmo_getaddrinfo(struct addrinfo **result, uint16_t family, uint16_t type, uint8_t proto, @@ -2844,7 +2830,7 @@ static int _osmo_getaddrinfo(struct addrinfo **result, } /* TODO move to osmocom/core/socket.c ? */ -int osmo_sockaddr_init(struct osmo_sockaddr *addr, +int sgsn_sockaddr_init(struct sgsn_sockaddr *addr, uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port) { @@ -2865,9 +2851,9 @@ int osmo_sockaddr_init(struct osmo_sockaddr *addr, return 0; } -int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, +int sgsn_sockaddr_to_strs(char *addr_str, size_t addr_str_len, char *port_str, size_t port_str_len, - const struct osmo_sockaddr *addr, + const struct sgsn_sockaddr *addr, int flags) { int rc; @@ -2896,14 +2882,14 @@ int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, return rc; } -const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, +const char *sgsn_sockaddr_to_strb(const struct sgsn_sockaddr *addr, char *buf, size_t buf_len) { + char portbuf[6]; const int portbuf_len = 6; OSMO_ASSERT(buf_len > portbuf_len); - char *portbuf = buf + buf_len - portbuf_len; buf_len -= portbuf_len; - if (osmo_sockaddr_to_strs(buf, buf_len, + if (sgsn_sockaddr_to_strs(buf, buf_len, portbuf, portbuf_len, addr, NI_NUMERICHOST | NI_NUMERICSERV)) @@ -2918,17 +2904,17 @@ const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, return buf; } -const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr) +const char *sgsn_sockaddr_to_str(const struct sgsn_sockaddr *addr) { static char buf[256]; - const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf)); + const char *result = sgsn_sockaddr_to_strb(addr, buf, sizeof(buf)); if (! result) return "(invalid)"; return result; } -int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, - const struct osmo_sockaddr *b) +int sgsn_sockaddr_cmp(const struct sgsn_sockaddr *a, + const struct sgsn_sockaddr *b) { if (a == b) return 0; @@ -2938,7 +2924,7 @@ int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, return 1; if (a->l != b->l) { /* Lengths are not the same, but determine the order. Will - * anyone ever sort a list by osmo_sockaddr though...? */ + * anyone ever sort a list by sgsn_sockaddr though...? */ int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l); if (cmp == 0) { if (a->l < b->l) @@ -2951,8 +2937,8 @@ int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, return memcmp(&a->a, &b->a, a->l); } -void osmo_sockaddr_copy(struct osmo_sockaddr *dst, - const struct osmo_sockaddr *src) +void sgsn_sockaddr_copy(struct sgsn_sockaddr *dst, + const struct sgsn_sockaddr *src) { OSMO_ASSERT(src->l <= sizeof(dst->a)); memcpy(&dst->a, &src->a, src->l); diff --git a/src/gtphub/gtphub_ares.c b/src/gtphub/gtphub_ares.c index 87dc860c4..774563db4 100644 --- a/src/gtphub/gtphub_ares.c +++ b/src/gtphub/gtphub_ares.c @@ -28,11 +28,12 @@ #include <string.h> #include <unistd.h> -#include <osmocom/sgsn/gtphub.h> +#include <osmocom/gtphub/gtphub.h> #include <osmocom/sgsn/debug.h> #include <osmocom/core/utils.h> #include <osmocom/gsm/apn.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> /* TODO split GRX ares from sgsn into a separate struct and allow use without * globals. */ @@ -56,8 +57,8 @@ struct ggsn_lookup { struct gtphub *hub; char imsi_str[GSM23003_IMSI_MAX_DIGITS+1]; - char apn_ni_str[GSM_APN_LENGTH]; - char apn_oi_str[GSM_APN_LENGTH]; + char apn_ni_str[APN_MAXLEN+1]; + char apn_oi_str[APN_MAXLEN+1]; int have_3dig_mnc; }; diff --git a/src/gtphub/gtphub_main.c b/src/gtphub/gtphub_main.c index f693f0993..9b0532b24 100644 --- a/src/gtphub/gtphub_main.c +++ b/src/gtphub/gtphub_main.c @@ -34,6 +34,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/utils.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/core/msgb.h> #include <osmocom/vty/logging.h> #include <osmocom/vty/telnet_interface.h> @@ -41,10 +42,10 @@ #include <osmocom/vty/misc.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gtphub.h> +#include <osmocom/gtphub/gtphub.h> #include <osmocom/sgsn/vty.h> -#include "../../bscconfig.h" +#include "../../config.h" #if BUILD_IU #include <osmocom/sigtran/osmo_ss7.h> @@ -95,11 +96,11 @@ void log_cfg(struct gtphub_cfg *cfg) } } -static void signal_handler(int signal) +static void signal_handler(int signum) { - fprintf(stdout, "signal %d received\n", signal); + fprintf(stdout, "signal %d received\n", signum); - switch (signal) { + switch (signum) { case SIGINT: case SIGTERM: osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); @@ -107,8 +108,16 @@ static void signal_handler(int signal) exit(0); break; case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ + /* in case of abort, we want to obtain a talloc report and + * then run default SIGABRT handler, who will generate coredump + * and abort the process. abort() should do this for us after we + * return, but program wouldn't exit if an external SIGABRT is + * received. + */ + talloc_report_full(osmo_gtphub_ctx, stderr); + signal(SIGABRT, SIG_DFL); + raise(SIGABRT); + break; case SIGUSR1: case SIGUSR2: talloc_report_full(osmo_gtphub_ctx, stderr); @@ -118,44 +127,12 @@ static void signal_handler(int signal) } } -#if BUILD_IU -int gtphub_vty_go_parent(struct vty *vty) -{ - switch (vty->node) { - default: - osmo_ss7_vty_go_parent(vty); - } - - return vty->node; -} -#endif - -int gtphub_vty_is_config_node(struct vty *vty, int node) -{ - /* Check if libosmo-sccp declares the node in - * question as config node */ -#if BUILD_IU - if (osmo_ss7_is_config_node(vty, node)) - return 1; -#endif - - switch (node) { - /* add items that are not config */ - case CONFIG_NODE: - return 0; - - default: - return 1; - } -} - static struct vty_app_info vty_info = { .name = "OsmoGTPhub", .version = PACKAGE_VERSION, #if BUILD_IU - .go_parent_cb = gtphub_vty_go_parent, + .go_parent_cb = osmo_ss7_vty_go_parent, #endif - .is_config_node = gtphub_vty_is_config_node, }; struct cmdline_cfg { @@ -322,6 +299,11 @@ static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv) break; } } + + if (argc > optind) { + fprintf(stderr, "Unsupported positional arguments on command line\n"); + exit(2); + } } int main(int argc, char **argv) @@ -372,8 +354,7 @@ int main(int argc, char **argv) } /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(), - OSMO_VTY_PORT_GTPHUB); + rc = telnet_init_default(osmo_gtphub_ctx, NULL, OSMO_VTY_PORT_GTPHUB); if (rc < 0) exit(1); diff --git a/src/gtphub/gtphub_sock.c b/src/gtphub/gtphub_sock.c index 1acd5a624..6f299d2d6 100644 --- a/src/gtphub/gtphub_sock.c +++ b/src/gtphub/gtphub_sock.c @@ -25,7 +25,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <osmocom/sgsn/gtphub.h> +#include <errno.h> +#include <string.h> + +#include <osmocom/core/utils.h> + +#include <osmocom/gtphub/gtphub.h> #include <osmocom/sgsn/debug.h> /* Convenience makro, note: only within this C file. */ @@ -33,13 +38,13 @@ LOGP(DGTPHUB, level, fmt, ##args) int gtphub_write(const struct osmo_fd *to, - const struct osmo_sockaddr *to_addr, + const struct sgsn_sockaddr *to_addr, const uint8_t *buf, size_t buf_len) { errno = 0; ssize_t sent = sendto(to->fd, buf, buf_len, 0, (struct sockaddr*)&to_addr->a, to_addr->l); - LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr)); + LOG(LOGL_DEBUG, "to %s\n", sgsn_sockaddr_to_str(to_addr)); if (sent == -1) { LOG(LOGL_ERROR, "error: %s\n", strerror(errno)); diff --git a/src/gtphub/gtphub_vty.c b/src/gtphub/gtphub_vty.c index abc08fd69..b5f102ae6 100644 --- a/src/gtphub/gtphub_vty.c +++ b/src/gtphub/gtphub_vty.c @@ -31,7 +31,7 @@ #include <osmocom/vty/misc.h> #include <osmocom/sgsn/vty.h> -#include <osmocom/sgsn/gtphub.h> +#include <osmocom/gtphub/gtphub.h> /* TODO split GRX ares from sgsn into a separate struct and allow use without * globals. */ diff --git a/src/sgsn/Makefile.am b/src/sgsn/Makefile.am index 6a7392baf..93b9fa058 100644 --- a/src/sgsn/Makefile.am +++ b/src/sgsn/Makefile.am @@ -40,12 +40,13 @@ bin_PROGRAMS = \ $(NULL) osmo_sgsn_SOURCES = \ - gprs_gb.c \ + apn.c \ + gprs_bssgp.c \ gprs_gmm_attach.c \ gprs_gmm.c \ gprs_gmm_fsm.c \ gprs_mm_state_gb_fsm.c \ - gprs_sgsn.c \ + gprs_ns.c \ gprs_sm.c \ gprs_sndcp.c \ gprs_sndcp_comp.c \ @@ -53,15 +54,21 @@ osmo_sgsn_SOURCES = \ gprs_sndcp_pcomp.c \ gprs_sndcp_vty.c \ gprs_sndcp_xid.c \ + gtp_ggsn.c \ + gtp_mme.c \ + sgsn.c \ sgsn_main.c \ sgsn_vty.c \ sgsn_libgtp.c \ gprs_llc.c \ gprs_llc_vty.c \ + mmctx.c \ + pdpctx.c \ sgsn_ctrl.c \ sgsn_auth.c \ gprs_subscriber.c \ sgsn_cdr.c \ + sgsn_rim.c \ slhc.c \ gprs_llc_xid.c \ v42bis.c \ diff --git a/src/sgsn/apn.c b/src/sgsn/apn.c new file mode 100644 index 000000000..a89d15866 --- /dev/null +++ b/src/sgsn/apn.c @@ -0,0 +1,123 @@ +/* APN contexts */ + +/* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <talloc.h> + +#include <osmocom/sgsn/apn.h> +#include <osmocom/sgsn/sgsn.h> + +static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) +{ + struct apn_ctx *actx; + + actx = talloc_zero(sgsn, struct apn_ctx); + if (!actx) + return NULL; + actx->name = talloc_strdup(actx, ap_name); + actx->imsi_prefix = talloc_strdup(actx, imsi_prefix); + + llist_add_tail(&actx->list, &sgsn->apn_list); + + return actx; +} + +void sgsn_apn_ctx_free(struct apn_ctx *actx) +{ + llist_del(&actx->list); + talloc_free(actx); +} + +struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi) +{ + struct apn_ctx *actx; + struct apn_ctx *found_actx = NULL; + size_t imsi_prio = 0; + size_t name_prio = 0; + size_t name_req_len = strlen(name); + + llist_for_each_entry(actx, &sgsn->apn_list, list) { + size_t name_ref_len, imsi_ref_len; + const char *name_ref_start, *name_match_start; + + imsi_ref_len = strlen(actx->imsi_prefix); + if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0) + continue; + + if (imsi_ref_len < imsi_prio) + continue; + + /* IMSI matches */ + + name_ref_start = &actx->name[0]; + if (name_ref_start[0] == '*') { + /* Suffix match */ + name_ref_start += 1; + name_ref_len = strlen(name_ref_start); + if (name_ref_len > name_req_len) + continue; + } else { + name_ref_len = strlen(name_ref_start); + if (name_ref_len != name_req_len) + continue; + } + + name_match_start = name + (name_req_len - name_ref_len); + if (strcasecmp(name_match_start, name_ref_start) != 0) + continue; + + /* IMSI and name match */ + + if (imsi_ref_len == imsi_prio && name_ref_len < name_prio) + /* Lower priority, skip */ + continue; + + imsi_prio = imsi_ref_len; + name_prio = name_ref_len; + found_actx = actx; + } + return found_actx; +} + +struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix) +{ + struct apn_ctx *actx; + + llist_for_each_entry(actx, &sgsn->apn_list, list) { + if (strcasecmp(name, actx->name) == 0 && + strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) + return actx; + } + return NULL; +} + +struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) +{ + struct apn_ctx *actx; + + actx = sgsn_apn_ctx_by_name(name, imsi_prefix); + if (!actx) + actx = sgsn_apn_ctx_alloc(name, imsi_prefix); + + return actx; +} diff --git a/src/sgsn/gprs_bssgp.c b/src/sgsn/gprs_bssgp.c new file mode 100644 index 000000000..5db751cce --- /dev/null +++ b/src/sgsn/gprs_bssgp.c @@ -0,0 +1,95 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <osmocom/core/prim.h> +#include <osmocom/core/rate_ctr.h> + +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_ns2.h> + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/sgsn_rim.h> +#include <osmocom/sgsn/mmctx.h> + +/* call-back function for the BSSGP protocol */ +int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph) +{ + struct osmo_bssgp_prim *bp; + bp = container_of(oph, struct osmo_bssgp_prim, oph); + + switch (oph->sap) { + case SAP_BSSGP_LL: + switch (oph->primitive) { + case PRIM_BSSGP_UL_UD: + return gprs_llc_rcvmsg(oph->msg, bp->tp); + } + break; + case SAP_BSSGP_GMM: + switch (oph->primitive) { + case PRIM_BSSGP_GMM_SUSPEND: + return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); + case PRIM_BSSGP_GMM_RESUME: + return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, + bp->u.resume.suspend_ref); + } + break; + case SAP_BSSGP_NM: + break; + case SAP_BSSGP_RIM: + return sgsn_rim_rx_from_gb(bp, oph->msg); + } + return 0; +} + +int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx) +{ + struct bssgp_paging_info pinfo; + int rc; + + /* FIXME: page whole routing area, not only the last known cell */ + + /* initiate PS PAGING procedure */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.mode = BSSGP_PAGING_PS; + pinfo.scope = BSSGP_PAGING_BVCI; + pinfo.bvci = mmctx->gb.bvci; + pinfo.imsi = mmctx->imsi; + pinfo.ptmsi = &mmctx->p_tmsi; + pinfo.drx_params = mmctx->drx_parms; + pinfo.qos[0] = 0; // FIXME + rc = bssgp_tx_paging(mmctx->gb.nsei, 0, &pinfo); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS)); + + return rc; +} + +/* called by the bssgp layer to send NS PDUs */ +int sgsn_bssgp_dispatch_ns_unitdata_req_cb(void *ctx, struct msgb *msg) +{ + struct gprs_ns2_inst *nsi = (struct gprs_ns2_inst *) ctx; + struct osmo_gprs_ns2_prim nsp = {}; + nsp.nsei = msgb_nsei(msg); + nsp.bvci = msgb_bvci(msg); + osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg); + return gprs_ns2_recv_prim(nsi, &nsp.oph); +} diff --git a/src/sgsn/gprs_gb.c b/src/sgsn/gprs_gb.c deleted file mode 100644 index d470cfab9..000000000 --- a/src/sgsn/gprs_gb.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Messages on the Gb interface (A/Gb mode) */ - -/* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org> - * (C) 2010 by On-Waves - * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * - * All Rights Reserved - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/core/rate_ctr.h> - -#include <osmocom/gprs/gprs_msgb.h> -#include <osmocom/gprs/gprs_bssgp.h> - -#include "bscconfig.h" - -#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> -#include <osmocom/sgsn/gprs_sgsn.h> -#include <osmocom/sgsn/gprs_gmm.h> -#include <osmocom/sgsn/gprs_sm.h> -#include <osmocom/sgsn/debug.h> - -/* Has to be called whenever any PDU (signaling, data, ...) has been received */ -void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx) { - if (mmctx->gb.llme) - osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_PDU_RECEPTION, NULL); -} - -/* Main entry point for incoming 04.08 GPRS messages from Gb */ -int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, - bool drop_cipherable) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - struct sgsn_mm_ctx *mmctx; - struct gprs_ra_id ra_id; - int rc = -EINVAL; - - bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); - mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); - if (mmctx) { - msgid2mmctx(mmctx, msg); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - mmctx->gb.llme = llme; - gprs_gb_recv_pdu(mmctx); - } - - /* MMCTX can be NULL */ - - switch (pdisc) { - case GSM48_PDISC_MM_GPRS: - rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable); - break; - case GSM48_PDISC_SM_GPRS: - rc = gsm0408_rcv_gsm(mmctx, msg, llme); - break; - default: - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Unknown GSM 04.08 discriminator 0x%02x: %s\n", - pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); - /* FIXME: return status message */ - break; - } - - /* MMCTX can be invalid */ - - return rc; -} - - -int gprs_gb_page_ps_ra(struct sgsn_mm_ctx *mmctx) -{ - struct bssgp_paging_info pinfo; - int rc; - - /* FIXME: page whole routing area, not only the last known cell */ - - /* initiate PS PAGING procedure */ - memset(&pinfo, 0, sizeof(pinfo)); - pinfo.mode = BSSGP_PAGING_PS; - pinfo.scope = BSSGP_PAGING_BVCI; - pinfo.bvci = mmctx->gb.bvci; - pinfo.imsi = mmctx->imsi; - pinfo.ptmsi = &mmctx->p_tmsi; - pinfo.drx_params = mmctx->drx_parms; - pinfo.qos[0] = 0; // FIXME - rc = bssgp_tx_paging(mmctx->gb.nsei, 0, &pinfo); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PAGING_PS]); - - return rc; -} diff --git a/src/sgsn/gprs_gmm.c b/src/sgsn/gprs_gmm.c index f62d0d1f2..c3f5cd3d2 100644 --- a/src/sgsn/gprs_gmm.c +++ b/src/sgsn/gprs_gmm.c @@ -30,7 +30,7 @@ #include <netinet/in.h> #include <arpa/inet.h> -#include "bscconfig.h" +#include "config.h" #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> @@ -41,13 +41,14 @@ #include <osmocom/core/utils.h> #include <osmocom/core/tdef.h> #include <osmocom/crypt/auth.h> +#include <osmocom/crypt/utran_cipher.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/gprs/gprs_bssgp.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/gprs_llc.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_utils.h> #include <osmocom/sgsn/gprs_subscriber.h> @@ -59,17 +60,17 @@ #include <osmocom/sgsn/signal.h> #include <osmocom/sgsn/gprs_sndcp.h> #include <osmocom/sgsn/gprs_ranap.h> +#include <osmocom/sgsn/gprs_sm.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> #include <pdp.h> #define PTMSI_ALLOC -extern struct sgsn_instance *sgsn; -extern void *tall_sgsn_ctx; - static const struct tlv_definition gsm48_gmm_att_tlvdef = { .def = { - [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, + [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 }, [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, @@ -78,6 +79,7 @@ static const struct tlv_definition gsm48_gmm_att_tlvdef = { [GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_RX_NPDU_NUM_LIST] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, @@ -131,7 +133,7 @@ int gsm48_gmm_sendmsg(struct msgb *msg, int command, struct sgsn_mm_ctx *mm, bool encryptable) { if (mm) { - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); + rate_ctr_inc(rate_ctr_group_get_ctr(mm->ctrg, GMM_CTR_PKTS_SIG_OUT)); #ifdef BUILD_IU if (mm->ran_type == MM_CTX_T_UTRAN_Iu) return ranap_iu_tx(msg, GPRS_SAPI_GMM); @@ -197,10 +199,10 @@ static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text) switch(ctx->ran_type) { case MM_CTX_T_UTRAN_Iu: - osmo_fsm_inst_dispatch(ctx->iu.mm_state_fsm, E_PMM_IMPLICIT_DETACH, NULL); + osmo_fsm_inst_dispatch(ctx->iu.mm_state_fsm, E_PMM_PS_DETACH, NULL); break; case MM_CTX_T_GERAN_Gb: - osmo_fsm_inst_dispatch(ctx->gb.mm_state_fsm, E_MM_IMPLICIT_DETACH, NULL); + osmo_fsm_inst_dispatch(ctx->gb.mm_state_fsm, E_MM_GPRS_DETACH, NULL); break; } @@ -215,7 +217,7 @@ static int _tx_status(struct msgb *msg, uint8_t cause, /* MMCTX might be NULL! */ - DEBUGP(DMM, "<- GPRS MM STATUS (cause: %s)\n", + DEBUGP(DMM, "<- GMM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); @@ -241,7 +243,7 @@ static int _tx_detach_req(struct msgb *msg, uint8_t detach_type, uint8_t cause, /* MMCTX might be NULL! */ - DEBUGP(DMM, "<- GPRS MM DETACH REQ (type: %s, cause: %s)\n", + DEBUGP(DMM, "<- GMM DETACH REQ (type: %s, cause: %s)\n", get_value_string(gprs_det_t_mt_strs, detach_type), get_value_string(gsm48_gmm_cause_names, cause)); @@ -280,14 +282,18 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT ACK"); struct gsm48_hdr *gh; struct gsm48_attach_ack *aa; - uint8_t *mid; unsigned long t; +#ifdef PTMSI_ALLOC + struct osmo_mobile_identity mi; + uint8_t *l; + int rc; +#endif #if 0 uint8_t *ptsig; #endif - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_ACKED]); + LOGMMCTXP(LOGL_INFO, mm, "<- GMM ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ATTACH_ACKED)); mmctx2msgid(msg, mm); @@ -300,7 +306,7 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) aa->att_result = 1; /* GPRS only */ t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1); aa->ra_upd_timer = gprs_secs_to_tmr_floor(t); - aa->radio_prio = 4; /* lowest */ + aa->radio_prio = 0x44; /* lowest */ gsm48_encode_ra(&aa->ra_id, &mm->ra); #if 0 @@ -321,14 +327,26 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); - mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + .tmsi = mm->p_tmsi, + }; + l = msgb_tl_put(msg, GSM48_IE_GMM_ALLOC_PTMSI); + rc = osmo_mobile_identity_encode_msgb(msg, &mi, false); + if (rc < 0) { + LOGMMCTXP(LOGL_ERROR, mm, "Cannot encode Mobile Identity\n"); + msgb_free(msg); + return -EINVAL; + } + *l = rc; #endif /* Optional: MS-identity (combined attach) */ /* Optional: GMM cause (partial attach result for combined attach) */ + /* Optional: Network feature support 10.5.5.23 */ + /* msgb_v_put(msg, GSM48_IE_GMM_NET_FEAT_SUPPORT | 0x00);*/ + return gsm48_gmm_sendmsg(msg, 0, mm, true); } @@ -338,9 +356,9 @@ static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause, { struct gsm48_hdr *gh; - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS ATTACH REJECT: %s\n", + LOGMMCTXP(LOGL_NOTICE, mm, "<- GMM ATTACH REJECT: %s\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REJECTED]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ATTACH_REJECTED)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; @@ -372,8 +390,8 @@ static int _tx_detach_ack(struct msgb *msg, uint8_t force_stby, /* MMCTX might be NULL! */ - DEBUGP(DMM, "<- GPRS MM DETACH ACC (force-standby: %d)\n", force_stby); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_ACKED]); + DEBUGP(DMM, "<- GMM DETACH ACC (force-standby: %d)\n", force_stby); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_DETACH_ACKED)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; @@ -405,7 +423,7 @@ int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ"); struct gsm48_hdr *gh; - LOGMMCTXP(LOGL_DEBUG, mm, "<- GPRS IDENTITY REQUEST: mi_type=%s\n", + LOGMMCTXP(LOGL_DEBUG, mm, "<- GMM IDENTITY REQUEST: mi_type=%s\n", gsm48_mi_type_name(id_type)); mmctx2msgid(msg, mm); @@ -429,6 +447,17 @@ static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm) return false; } +static enum gprs_ciph_algo gprs_ms_net_select_best_gea(uint8_t net_mask, uint8_t ms_mask) { + uint8_t common_mask = net_mask & ms_mask; + uint8_t r = 0; + + while (common_mask >>= 1) { + r++; + } + + return r; +} + /* 3GPP TS 24.008 § 9.4.9: Authentication and Ciphering Request */ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, const struct osmo_auth_vector *vec, @@ -440,7 +469,7 @@ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *m_rand, *m_cksn, rbyte; int rc; - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s," + LOGMMCTXP(LOGL_INFO, mm, "<- GMM AUTH AND CIPHERING REQ (rand = %s," " mmctx_is_r99=%d, vec->auth_types=0x%x", osmo_hexdump(vec->rand, sizeof(vec->rand)), mmctx_is_r99(mm), vec->auth_types); @@ -503,7 +532,7 @@ static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REJ"); struct gsm48_hdr *gh; - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS AUTH AND CIPH REJECT\n"); + LOGMMCTXP(LOGL_NOTICE, mm, "<- GMM AUTH AND CIPH REJECT\n"); mmctx2msgid(msg, mm); @@ -583,7 +612,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx, uint8_t res_len; int rc; - LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n"); + LOGMMCTXP(LOGL_INFO, ctx, "-> GMM AUTH AND CIPH RESPONSE\n"); if (ctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { LOGMMCTXP(LOGL_NOTICE, ctx, @@ -634,7 +663,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx, ctx->sec_ctx = check_auth_resp(ctx, false, &at->vec, res, res_len); if (!sgsn_mm_ctx_is_authenticated(ctx)) { rc = gsm48_tx_gmm_auth_ciph_rej(ctx); - mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT"); + mm_ctx_cleanup_free(ctx, "GMM AUTH AND CIPH REJECT"); return rc; } @@ -657,7 +686,7 @@ static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx, const uint8_t *auts; int rc; - LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH FAILURE (cause = %s)\n", + LOGMMCTXP(LOGL_INFO, ctx, "-> GMM AUTH AND CIPH FAILURE (cause = %s)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0); @@ -697,7 +726,7 @@ static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx, LOGMMCTXP(LOGL_NOTICE, ctx, "Authentication failed\n"); rc = gsm48_tx_gmm_auth_ciph_rej(ctx); - mm_ctx_cleanup_free(ctx, "GPRS AUTH FAILURE"); + mm_ctx_cleanup_free(ctx, "GMM AUTH FAILURE"); return rc; } @@ -776,7 +805,7 @@ static int gsm48_tx_gmm_service_ack(struct sgsn_mm_ctx *mm) struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE ACK"); struct gsm48_hdr *gh; - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS SERVICE ACCEPT (P-TMSI=0x%08x)\n", mm->p_tmsi); + LOGMMCTXP(LOGL_INFO, mm, "<- GMM SERVICE ACCEPT (P-TMSI=0x%08x)\n", mm->p_tmsi); mmctx2msgid(msg, mm); @@ -797,7 +826,7 @@ static int _tx_gmm_service_rej(struct msgb *msg, uint8_t gmm_cause, { struct gsm48_hdr *gh; - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS SERVICE REJECT: %s\n", + LOGMMCTXP(LOGL_NOTICE, mm, "<- GMM SERVICE REJECT: %s\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); @@ -889,7 +918,13 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) /* The MS is authorized */ #ifdef BUILD_IU if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) { - rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key); + /* Is any encryption above UEA0 enabled? */ + bool send_ck = sgsn->cfg.uea_encryption_mask > (1 << OSMO_UTRAN_UEA0); + LOGMMCTXP(LOGL_DEBUG, ctx, "Iu Security Mode Command: %s encryption key (UEA encryption mask = 0x%x)\n", + send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask); + /* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this + * is not possible in the iu_client API. See OS#5487. */ + rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key); ctx->iu.new_key = 0; return rc; } @@ -911,16 +946,16 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) ctx->t3350_mode = GMM_T3350_MODE_ATT; #else memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mmctx; + sig_data.mm = ctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); - osmo_fsm_inst_dispatch(mm->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL); + osmo_fsm_inst_dispatch(ctx->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL); #endif return gsm48_tx_gmm_att_ack(ctx); #ifdef BUILD_IU case GSM48_MT_GMM_SERVICE_REQ: ctx->pending_req = 0; - osmo_fsm_inst_dispatch(ctx->iu.mm_state_fsm, E_PMM_PS_ATTACH, NULL); + osmo_fsm_inst_dispatch(ctx->iu.mm_state_fsm, E_PMM_PS_CONN_ESTABLISH, NULL); rc = gsm48_tx_gmm_service_ack(ctx); if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING) @@ -1026,31 +1061,35 @@ void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *ctx, int gmm_cause) static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - long mi_typel = mi_type; - char mi_string[GSM48_MI_SIZE]; + long mi_typel; + char mi_log_string[32]; + struct osmo_mobile_identity mi; - gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); if (!ctx) { DEBUGP(DMM, "from unknown TLLI 0x%08x?!? This should not happen\n", msgb_tlli(msg)); return -EINVAL; } - LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); + if (osmo_mobile_identity_decode(&mi, &gh->data[1], gh->data[0], false)) { + LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM IDENTITY RESPONSE: cannot decode Mobile Identity\n"); + return -EINVAL; + } + osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi); + + LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI=%s\n", mi_log_string); if (ctx->t3370_id_type == GSM_MI_TYPE_NONE) { LOGMMCTXP(LOGL_NOTICE, ctx, - "Got unexpected IDENTITY RESPONSE: MI(%s)=%s, " + "Got unexpected IDENTITY RESPONSE: MI=%s, " "ignoring message\n", - gsm48_mi_type_name(mi_type), mi_string); + mi_log_string); return -EINVAL; } - if (mi_type == ctx->t3370_id_type) + if (mi.type == ctx->t3370_id_type) mmctx_timer_stop(ctx, 3370); - switch (mi_type) { + switch (mi.type) { case GSM_MI_TYPE_IMSI: /* we already have a mm context with current TLLI, but no * P-TMSI / IMSI yet. What we now need to do is to fill @@ -1058,7 +1097,7 @@ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) if (strlen(ctx->imsi) == 0) { /* Check if we already have a MM context for this IMSI */ struct sgsn_mm_ctx *ictx; - ictx = sgsn_mm_ctx_by_imsi(mi_string); + ictx = sgsn_mm_ctx_by_imsi(mi.imsi); if (ictx) { /* Handle it like in gsm48_rx_gmm_det_req, * except that no messages are sent to the BSS */ @@ -1067,19 +1106,20 @@ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) "p_tmsi_old=0x%08x\n", ictx->p_tmsi); - mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use"); + mm_ctx_cleanup_free(ictx, "GMM IMSI re-use"); } } - osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); + OSMO_STRLCPY_ARRAY(ctx->imsi, mi.imsi); break; case GSM_MI_TYPE_IMEI: - osmo_strlcpy(ctx->imei, mi_string, sizeof(ctx->imei)); + OSMO_STRLCPY_ARRAY(ctx->imei, mi.imei); break; case GSM_MI_TYPE_IMEISV: break; } /* Check if we can let the mobile station enter */ + mi_typel = mi.type; return osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_IDEN_RESP_RECV, (void *)mi_typel); } @@ -1099,23 +1139,65 @@ static inline void ptmsi_update(struct sgsn_mm_ctx *ctx) osmo_fsm_inst_dispatch(ctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL); } +/* Detect if RAT has changed */ +static bool mmctx_did_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg) +{ + if (MSG_IU_UE_CTX(msg) && mmctx->ran_type != MM_CTX_T_UTRAN_Iu) + return true; + if (!MSG_IU_UE_CTX(msg) && mmctx->ran_type != MM_CTX_T_GERAN_Gb) + return true; + return false; +} + +/* Notify the FSM of a RAT change */ +static void mmctx_handle_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) +{ + struct gmm_rat_change_data rat_chg = { + .llme = llme + }; + + rat_chg.new_ran_type = MSG_IU_UE_CTX(msg) ? MM_CTX_T_UTRAN_Iu : MM_CTX_T_GERAN_Gb; + + if (rat_chg.new_ran_type != mmctx->ran_type) + osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_RAT_CHANGE, (void *) &rat_chg); + else + LOGMMCTXP(LOGL_ERROR, mmctx, "RAT didn't change or not implemented (ran_type=%u, " + "msg_iu_ue_ctx=%p\n", mmctx->ran_type, MSG_IU_UE_CTX(msg)); + +} + +static uint8_t gprs_ms_net_cap_gea_mask(const uint8_t *ms_net_cap, uint8_t cap_len) +{ + uint8_t mask = (1 << GPRS_ALGO_GEA0); + mask |= (0x80 & ms_net_cap[0]) ? (1 << GPRS_ALGO_GEA1) : 0; + + if (cap_len < 2) + return mask; + + /* extended GEA bits start from 2nd bit of the next byte */ + mask |= (0x40 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA2) : 0; + mask |= (0x20 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA3) : 0; + mask |= (0x10 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA4) : 0; + return mask; +} + /* 3GPP TS 24.008 § 9.4.1 Attach request */ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data, *msnc, *mi, *ms_ra_acc_cap; - uint8_t msnc_len, att_type, mi_len, mi_type, ms_ra_acc_cap_len; + uint8_t *cur = gh->data, *msnc, *mi_data, *ms_ra_acc_cap; + uint8_t msnc_len, att_type, mi_len, ms_ra_acc_cap_len; uint16_t drx_par; - uint32_t tmsi; - char mi_string[GSM48_MI_SIZE]; + char mi_log_string[32]; struct gprs_ra_id ra_id; uint16_t cid = 0; enum gsm48_gmm_cause reject_cause; + struct osmo_mobile_identity mi; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST "); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ATTACH_REQUEST)); /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either * with a foreign TLLI (P-TMSI that was allocated to the MS before), @@ -1155,15 +1237,15 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ mi_len = *cur++; - mi = cur; - if (mi_len > 8) - goto err_inval; - mi_type = *mi & GSM_MI_TYPE_MASK; + mi_data = cur; cur += mi_len; - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + rc = osmo_mobile_identity_decode(&mi, mi_data, mi_len, false); + if (rc) + goto err_inval; + osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi); - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_log_string, get_value_string(gprs_att_t_strs, att_type)); /* Old routing area identification 10.5.5.15. Skip it */ @@ -1180,11 +1262,11 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */ - switch (mi_type) { + switch (mi.type) { case GSM_MI_TYPE_IMSI: /* Try to find MM context based on IMSI */ if (!ctx) - ctx = sgsn_mm_ctx_by_imsi(mi_string); + ctx = sgsn_mm_ctx_by_imsi(mi.imsi); if (!ctx) { if (MSG_IU_UE_CTX(msg)) ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg)); @@ -1194,15 +1276,13 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } - osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); + OSMO_STRLCPY_ARRAY(ctx->imsi, mi.imsi); } break; case GSM_MI_TYPE_TMSI: - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); /* Try to find MM context based on P-TMSI */ if (!ctx) - ctx = sgsn_mm_ctx_by_ptmsi(tmsi); + ctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi); if (!ctx) { /* Allocate a context as most of our code expects one. * Context will not have an IMSI ultil ID RESP is received */ @@ -1214,16 +1294,19 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } - ctx->p_tmsi = tmsi; + ctx->p_tmsi = mi.tmsi; } break; default: LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with " - "MI type %s\n", gsm48_mi_type_name(mi_type)); + "MI %s\n", mi_log_string); reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; goto rejected; } + if (mmctx_did_rat_change(ctx, msg)) + mmctx_handle_rat_change(ctx, msg, llme); + if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { ctx->gb.tlli = msgb_tlli(msg); ctx->gb.llme = llme; @@ -1241,15 +1324,27 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, ctx->ms_radio_access_capa.len); ctx->ms_network_capa.len = msnc_len; memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); - if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len, - ctx->ciph_algo)) { + + ctx->ue_cipher_mask = gprs_ms_net_cap_gea_mask(ctx->ms_network_capa.buf, msnc_len); + + if (!(ctx->ue_cipher_mask & sgsn->cfg.gea_encryption_mask)) { reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI " - "type %s because MS do not support required %s " - "encryption\n", gsm48_mi_type_name(mi_type), - get_value_string(gprs_cipher_names,ctx->ciph_algo)); + "%s because MS do not support required encryption, mask UE:0x%02x NW:0x%02x \n", + mi_log_string, ctx->ue_cipher_mask, sgsn->cfg.gea_encryption_mask); goto rejected; } + + /* just assume that everythig is fine if the phone offers a5/4: + * it requires a valid umts security context which we can only have after + * 1) IDENTITY REQUEST to know what to ask the HLR for + * 2) and AUTHENTICATION AND CIPHERING REQUEST + * ... but 2) already requires selecting a cipher mode. + * So let's just assume we will have the auth data required to make it work. + */ + + ctx->ciph_algo = gprs_ms_net_select_best_gea(ctx->ue_cipher_mask, sgsn->cfg.gea_encryption_mask); + #ifdef PTMSI_ALLOC /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ ptmsi_update(ctx); @@ -1281,7 +1376,7 @@ rejected: get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_att_rej_oldmsg(msg, reject_cause); if (ctx) - mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJ"); + mm_ctx_cleanup_free(ctx, "GMM ATTACH REJ"); else if (llme) gprs_llgmm_unassign(llme); @@ -1294,7 +1389,7 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx) { struct sgsn_signal_data sig_data; /* only in case SGSN offered new P-TMSI */ - LOGMMCTXP(LOGL_INFO, mmctx, "-> ATTACH COMPLETE\n"); + LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM ATTACH COMPLETE\n"); #ifdef BUILD_IU if (mmctx->iu.ue_ctx) { @@ -1361,7 +1456,7 @@ static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) power_off = gh->data[0] & 0x8; /* FIXME: In 24.008 there is an optional P-TMSI and P-TMSI signature IE */ - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_DETACH_REQUEST)); LOGMMCTXP(LOGL_INFO, ctx, "-> GMM DETACH REQUEST TLLI=0x%08x type=%s %s\n", msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type), power_off ? "Power-off" : ""); @@ -1381,7 +1476,7 @@ static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = ctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_DETACH, &sig_data); - mm_ctx_cleanup_free(ctx, "GPRS DETACH REQUEST"); + mm_ctx_cleanup_free(ctx, "GMM DETACH REQUEST"); } return rc; @@ -1393,11 +1488,15 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UPD ACK"); struct gsm48_hdr *gh; struct gsm48_ra_upd_ack *rua; - uint8_t *mid; unsigned long t; +#ifdef PTMSI_ALLOC + uint8_t *l; + int rc; + struct osmo_mobile_identity mi; +#endif - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_ACKED]); - LOGMMCTXP(LOGL_INFO, mm, "<- ROUTING AREA UPDATE ACCEPT\n"); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ROUTING_AREA_ACKED)); + LOGMMCTXP(LOGL_INFO, mm, "<- GMM ROUTING AREA UPDATE ACCEPT\n"); mmctx2msgid(msg, mm); @@ -1424,9 +1523,17 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); - mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + .tmsi = mm->p_tmsi, + }; + l = msgb_tl_put(msg, GSM48_IE_GMM_ALLOC_PTMSI); + rc = osmo_mobile_identity_encode_msgb(msg, &mi, false); + if (rc < 0) { + msgb_free(msg); + return -EINVAL; + } + *l = rc; #endif /* Optional: Negotiated READY timer value */ @@ -1444,7 +1551,7 @@ int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) struct gsm48_hdr *gh; LOGP(DMM, LOGL_NOTICE, "<- ROUTING AREA UPDATE REJECT\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REJECT]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ROUTING_AREA_REJECT)); gmm_copy_id(msg, old_msg); @@ -1470,21 +1577,19 @@ static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, * being in state PDP-INACTIVE. */ llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { - if (pdp->nsapi < 8) { - if (!(pdp_status[0] & (1 << pdp->nsapi))) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " - "due to PDP CTX STATUS IE= 0x%02x%02x\n", - pdp->nsapi, pdp_status[1], pdp_status[0]); - sgsn_delete_pdp_ctx(pdp); - } - } else { - if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " - "due to PDP CTX STATUS IE= 0x%02x%02x\n", - pdp->nsapi, pdp_status[1], pdp_status[0]); - sgsn_delete_pdp_ctx(pdp); - } - } + bool inactive = (pdp->nsapi < 8) ? + !(pdp_status[0] & (1 << pdp->nsapi)) : + !(pdp_status[1] & (1 << (pdp->nsapi - 8))); + if (!inactive) + continue; + + LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " + "due to PDP CTX STATUS IE=0x%02x%02x\n", + pdp->nsapi, pdp_status[1], pdp_status[0]); + if (pdp->ggsn) + sgsn_delete_pdp_ctx(pdp); + else /* GTP side already detached, freeing */ + sgsn_pdp_ctx_free(pdp); } } @@ -1526,7 +1631,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, /* Update Type 10.5.5.18 */ upd_type = *cur++ & 0x07; - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ROUTING_AREA_REQUEST)); LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM RA UPDATE REQUEST type=\"%s\"\n", get_value_string(gprs_upd_t_strs, upd_type)); @@ -1576,19 +1681,14 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, } else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) { #ifdef BUILD_IU /* In Iu mode search only for ptmsi */ - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_len = TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI); - const uint8_t *mi = TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI); - uint8_t mi_type = *mi & GSM_MI_TYPE_MASK; - uint32_t tmsi; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - if (mi_type == GSM_MI_TYPE_TMSI) { - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); - mmctx = sgsn_mm_ctx_by_ptmsi(tmsi); + struct osmo_mobile_identity mi; + if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI), + TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI), false) + || mi.type != GSM_MI_TYPE_TMSI) { + LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR, "Cannot decode P-TMSI\n"); + goto rejected; } + mmctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi); #else LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR, "Rejecting GMM RA Update Request: No Iu support\n"); @@ -1604,21 +1704,36 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, mmctx->p_tmsi, mmctx->p_tmsi_old, mmctx->gb.tlli, mmctx->gb.tlli_new, osmo_rai_name(&mmctx->ra)); - osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL); + /* A RAT change will trigger the common procedure + * below after handling the RAT change. Protect it + * here from being called twice */ + if (!mmctx_did_rat_change(mmctx, msg)) + osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL); + } } else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) || mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED) { - /* We cannot use the mmctx */ - LOGMMCTXP(LOGL_INFO, mmctx, - "The MM context cannot be used, RA: %s\n", - osmo_rai_name(&mmctx->ra)); - /* mmctx is set to NULL and gprs_llgmm_unassign(llme) will be - called below, let's make sure we don't keep dangling llme - pointers in mmctx (OS#3957, OS#4245). */ - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) - mmctx->gb.llme = NULL; - mmctx = NULL; + /* We've received either a RAU for a MS which isn't registered + * or a RAU with an unknown RA ID. As long the SGSN doesn't support + * PS handover we treat this as invalid RAU */ + struct gprs_ra_id new_ra_id; + char new_ra[32]; + + bssgp_parse_cell_id(&new_ra_id, msgb_bcid(msg)); + osmo_rai_name_buf(new_ra, sizeof(new_ra), &new_ra_id); + + if (mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED) + LOGMMCTXP(LOGL_INFO, mmctx, + "Rejecting RAU - GMM state is deregistered. Old RA: %s New RA: %s\n", + osmo_rai_name(&old_ra_id), new_ra); + else + LOGMMCTXP(LOGL_INFO, mmctx, + "Rejecting RAU - Old RA doesn't match MM. Old RA: %s New RA: %s\n", + osmo_rai_name(&old_ra_id), new_ra); + + reject_cause = GMM_CAUSE_IMPL_DETACHED; + goto rejected; } if (!mmctx) { @@ -1636,21 +1751,30 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, goto rejected; } + if (mmctx_did_rat_change(mmctx, msg)) { + mmctx_handle_rat_change(mmctx, msg, llme); + osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL); + } + /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ msgid2mmctx(mmctx, msg); /* Bump the statistics of received signalling msgs for this MM context */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN)); /* Update the MM context with the new RA-ID */ - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { + if (mmctx->ran_type == MM_CTX_T_GERAN_Gb && msgb_bcid(msg)) { bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); /* Update the MM context with the new (i.e. foreign) TLLI */ mmctx->gb.tlli = msgb_tlli(msg); } + /* Update the MM context with the new DRX params */ + if (TLVP_PRESENT(&tp, GSM48_IE_GMM_DRX_PARAM)) + memcpy(&mmctx->drx_parms, TLVP_VAL(&tp, GSM48_IE_GMM_DRX_PARAM), sizeof(mmctx->drx_parms)); + /* FIXME: Update the MM context with the MS radio acc capabilities */ /* FIXME: Update the MM context with the MS network capabilities */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_RA_UPDATE)); #ifdef PTMSI_ALLOC ptmsi_update(mmctx); @@ -1660,7 +1784,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, mmctx_timer_start(mmctx, 3350); #else /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ - osmo_fsm_inst_dispatch(mm->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL); + osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL); memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; @@ -1696,7 +1820,7 @@ rejected: get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_ra_upd_rej(msg, reject_cause); if (mmctx) - mm_ctx_cleanup_free(mmctx, "GPRS RA UPDATE REJ"); + mm_ctx_cleanup_free(mmctx, "GMM RA UPDATE REJ"); else if (llme) gprs_llgmm_unassign(llme); #ifdef BUILD_IU @@ -1762,11 +1886,11 @@ static int gsm48_rx_gmm_ptmsi_reall_compl(struct sgsn_mm_ctx *mmctx) static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data, *mi; - uint8_t service_type, mi_len, mi_type; - uint32_t tmsi; + uint8_t *cur = gh->data, *mi_data; + uint8_t service_type, mi_len; struct tlv_parsed tp; - char mi_string[GSM48_MI_SIZE]; + struct osmo_mobile_identity mi; + char mi_log_string[32]; enum gsm48_gmm_cause reject_cause; int rc; @@ -1786,15 +1910,14 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ mi_len = *cur++; - mi = cur; - if (mi_len > 8) - goto err_inval; - mi_type = *mi & GSM_MI_TYPE_MASK; + mi_data = cur; cur += mi_len; + rc = osmo_mobile_identity_decode(&mi, mi_data, mi_len, false); + if (rc) + goto err_inval; + osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi); - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_log_string, get_value_string(gprs_service_t_strs, service_type)); LOGPC(DMM, LOGL_INFO, "\n"); @@ -1802,11 +1925,11 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) /* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */ tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); - switch (mi_type) { + switch (mi.type) { case GSM_MI_TYPE_IMSI: /* Try to find MM context based on IMSI */ if (!ctx) - ctx = sgsn_mm_ctx_by_imsi(mi_string); + ctx = sgsn_mm_ctx_by_imsi(mi.imsi); if (!ctx) { /* FIXME: We need to have a context for service request? */ reject_cause = GMM_CAUSE_IMPL_DETACHED; @@ -1815,11 +1938,9 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) msgid2mmctx(ctx, msg); break; case GSM_MI_TYPE_TMSI: - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); /* Try to find MM context based on P-TMSI */ if (!ctx) - ctx = sgsn_mm_ctx_by_ptmsi(tmsi); + ctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi); if (!ctx) { /* FIXME: We need to have a context for service request? */ reject_cause = GMM_CAUSE_IMPL_DETACHED; @@ -1829,7 +1950,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) break; default: LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with " - "MI type %s\n", gsm48_mi_type_name(mi_type)); + "MI %s\n", mi_log_string); reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; goto rejected; } @@ -1881,7 +2002,7 @@ static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); - LOGMMCTXP(LOGL_INFO, mmctx, "-> GPRS MM STATUS (cause: %s)\n", + LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, gh->data[0])); return 0; @@ -1944,6 +2065,23 @@ int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, return rc; } + /* A RAT change is only expected/allowed for RAU/Attach Req */ + if (mmctx && mmctx_did_rat_change(mmctx, msg)) { + switch (gh->msg_type) { + case GSM48_MT_GMM_RA_UPD_REQ: + case GSM48_MT_GMM_ATTACH_REQ: + break; + default: + /* This shouldn't happen with other message types and + * we need to error out to prevent a crash */ + LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping GMM %s which was received on different " + "RAT (mmctx ran_type=%u, msg_iu_ue_ctx=%p\n", + get_value_string(gprs_msgt_gmm_names, gh->msg_type), + mmctx->ran_type, MSG_IU_UE_CTX(msg)); + return -EINVAL; + } + } + /* * For a few messages, mmctx may be NULL. For most, we want to ensure a * non-NULL mmctx. At the same time, we want to keep the message @@ -1983,7 +2121,7 @@ int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, if (!mmctx) goto null_mmctx; LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n"); - mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK"); + mm_ctx_cleanup_free(mmctx, "GMM DETACH ACK"); rc = 0; break; case GSM48_MT_GMM_ATTACH_COMPL: @@ -2023,9 +2161,9 @@ int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, null_mmctx: LOGGBIUP(llme, msg, LOGL_ERROR, - "Received GSM 04.08 message type 0x%02x," + "Received GSM 04.08 message type %s," " but no MM context available\n", - gh->msg_type); + get_value_string(gprs_msgt_gmm_names, gh->msg_type)); return -EINVAL; } @@ -2092,7 +2230,7 @@ static void mmctx_timer_cb(void *_mm) if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3370 expired >= 5 times\n"); gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); - mm_ctx_cleanup_free(mm, "GPRS ATTACH REJECT (T3370)"); + mm_ctx_cleanup_free(mm, "GMM ATTACH REJECT (T3370)"); break; } /* re-tranmit IDENTITY REQUEST and re-start timer */ @@ -2180,3 +2318,51 @@ int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_RESUME, NULL); return 0; } + +/* Has to be called whenever any PDU (signaling, data, ...) has been received */ +void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx, const struct msgb *msg) +{ + msgid2mmctx(mmctx, msg); + if (mmctx->gb.llme) + osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_PDU_RECEPTION, NULL); +} + +/* Main entry point for incoming 04.08 GPRS messages from Gb */ +int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, + bool drop_cipherable) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t pdisc = gsm48_hdr_pdisc(gh); + struct sgsn_mm_ctx *mmctx; + struct gprs_ra_id ra_id; + int rc = -EINVAL; + + bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); + if (mmctx) { + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN)); + mmctx->gb.llme = llme; + gprs_gb_recv_pdu(mmctx, msg); + } + + /* MMCTX can be NULL */ + + switch (pdisc) { + case GSM48_PDISC_MM_GPRS: + rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable); + break; + case GSM48_PDISC_SM_GPRS: + rc = gsm0408_rcv_gsm(mmctx, msg, llme); + break; + default: + LOGMMCTXP(LOGL_NOTICE, mmctx, + "Unknown GSM 04.08 discriminator 0x%02x: %s\n", + pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); + /* FIXME: return status message */ + break; + } + + /* MMCTX can be invalid */ + + return rc; +} diff --git a/src/sgsn/gprs_gmm_attach.c b/src/sgsn/gprs_gmm_attach.c index c903a016b..708ea8f23 100644 --- a/src/sgsn/gprs_gmm_attach.c +++ b/src/sgsn/gprs_gmm_attach.c @@ -1,10 +1,12 @@ #include <osmocom/core/tdef.h> +#include <osmocom/crypt/utran_cipher.h> #include <osmocom/sgsn/gprs_gmm_attach.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/sgsn.h> #define X(s) (1 << (s)) @@ -257,6 +259,7 @@ static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_ { #ifdef BUILD_IU struct sgsn_mm_ctx *ctx = fi->priv; + bool send_ck; /* TODO: shouldn't this set always? not only when the integrity_active? */ if (ctx->iu.ue_ctx->integrity_active) { @@ -264,7 +267,14 @@ static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_ return; } - ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key); + /* Is any encryption above UEA0 enabled? */ + send_ck = sgsn->cfg.uea_encryption_mask > (1 << OSMO_UTRAN_UEA0); + LOGMMCTXP(LOGL_DEBUG, ctx, "Iu Security Mode Command: %s encryption key (UEA encryption mask = 0x%x)\n", + send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask); + + /* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this + * is not possible in the iu_client API. See OS#5487. */ + ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key); ctx->iu.new_key = 0; #endif } @@ -272,6 +282,13 @@ static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_ static void st_iu_security_cmd(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch(event) { + case E_VLR_ANSWERED: + /* We may receive an event due to rx + * OSMO_GSUP_MSGT_INSERT_DATA_REQUEST here. Do nothing, update of + * subscriber is done by lower layers before event is signalled. + * In this state we simply wait for E_IU_SECURITY_CMD_COMPLETE + */ + break; case E_IU_SECURITY_CMD_COMPLETE: gmm_attach_fsm_state_chg(fi, ST_ACCEPT); break; @@ -308,7 +325,7 @@ static struct osmo_fsm_state gmm_attach_req_fsm_states[] = { .action = st_auth, }, [ST_IU_SECURITY_CMD] = { - .in_event_mask = X(E_IU_SECURITY_CMD_COMPLETE), + .in_event_mask = X(E_IU_SECURITY_CMD_COMPLETE) | X(E_VLR_ANSWERED), .out_state_mask = X(ST_INIT) | X(ST_AUTH) | X(ST_ACCEPT) | X(ST_REJECT), .name = "IuSecurityCommand", .onenter = st_iu_security_cmd_on_enter, diff --git a/src/sgsn/gprs_gmm_fsm.c b/src/sgsn/gprs_gmm_fsm.c index 94ecb50ef..57e1ec3bb 100644 --- a/src/sgsn/gprs_gmm_fsm.c +++ b/src/sgsn/gprs_gmm_fsm.c @@ -1,6 +1,30 @@ +/* GMM mobility management states on the network side, 3GPP TS 24.008 § 4.1.3.3 */ +/* + * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ #include <osmocom/core/tdef.h> #include <osmocom/sgsn/gprs_gmm_fsm.h> +#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> +#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/sgsn.h> @@ -33,6 +57,10 @@ static void st_gmm_deregistered(struct osmo_fsm_inst *fi, uint32_t event, void * static void st_gmm_common_proc_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch(event) { + case E_GMM_COMMON_PROC_INIT_REQ: + /* MS may retransmit GPRS Attach Request if for some reason + * CommonProcedure didn't go forward correctly */ + break; /* TODO: events not used case E_GMM_LOWER_LAYER_FAILED: case E_GMM_COMMON_PROC_FAILED: @@ -52,6 +80,11 @@ static void st_gmm_registered_normal(struct osmo_fsm_inst *fi, uint32_t event, v case E_GMM_COMMON_PROC_INIT_REQ: gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT); break; + case E_GMM_COMMON_PROC_SUCCESS: + /* If we were moved from ST_GMM_COMMON_PROC_INIT here by + * E_GMM_ATTACH_SUCCESS instead of E_GMM_COMMON_PROC_SUCCESS then we'll receive the latter here: + * we should simply ignore it */ + break; /* case E_GMM_NET_INIT_DETACH_REQ: gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED_INIT); break; */ @@ -67,9 +100,12 @@ static void st_gmm_registered_normal(struct osmo_fsm_inst *fi, uint32_t event, v static void st_gmm_registered_suspended(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch(event) { - case E_GMM_RESUME: + case E_GMM_RESUME: /* explicit BSSGP RESUME from BSS */ gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_NORMAL); break; + case E_GMM_COMMON_PROC_INIT_REQ: /* implicit resume from MS */ + gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT); + break; } } @@ -99,7 +135,8 @@ static struct osmo_fsm_state gmm_fsm_states[] = { /* X(E_GMM_LOWER_LAYER_FAILED) | */ /* X(E_GMM_COMMON_PROC_FAILED) | */ X(E_GMM_COMMON_PROC_SUCCESS) | - X(E_GMM_ATTACH_SUCCESS), + X(E_GMM_ATTACH_SUCCESS) | + X(E_GMM_COMMON_PROC_INIT_REQ), .out_state_mask = X(ST_GMM_DEREGISTERED) | X(ST_GMM_REGISTERED_NORMAL), @@ -109,6 +146,7 @@ static struct osmo_fsm_state gmm_fsm_states[] = { [ST_GMM_REGISTERED_NORMAL] = { .in_event_mask = X(E_GMM_COMMON_PROC_INIT_REQ) | + X(E_GMM_COMMON_PROC_SUCCESS) | /* X(E_GMM_NET_INIT_DETACH_REQ) | */ /* X(E_GMM_MS_INIT_DETACH_REQ) | */ X(E_GMM_SUSPEND), @@ -121,10 +159,12 @@ static struct osmo_fsm_state gmm_fsm_states[] = { .action = st_gmm_registered_normal, }, [ST_GMM_REGISTERED_SUSPENDED] = { - .in_event_mask = X(E_GMM_RESUME), + .in_event_mask = X(E_GMM_RESUME) | + X(E_GMM_COMMON_PROC_INIT_REQ), .out_state_mask = X(ST_GMM_DEREGISTERED) | - X(ST_GMM_REGISTERED_NORMAL), + X(ST_GMM_REGISTERED_NORMAL) | + X(ST_GMM_COMMON_PROC_INIT), .name = "Registered.SUSPENDED", .action = st_gmm_registered_suspended, }, @@ -149,11 +189,32 @@ const struct value_string gmm_fsm_event_names[] = { /* OSMO_VALUE_STRING(E_GMM_DETACH_ACCEPTED), */ OSMO_VALUE_STRING(E_GMM_SUSPEND), OSMO_VALUE_STRING(E_GMM_CLEANUP), + OSMO_VALUE_STRING(E_GMM_RAT_CHANGE), { 0, NULL } }; void gmm_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + struct sgsn_mm_ctx *mmctx = fi->priv; + struct gmm_rat_change_data *rat_chg = (struct gmm_rat_change_data *)data; + switch (event) { + case E_GMM_RAT_CHANGE: + + switch (fi->state) { + case ST_GMM_COMMON_PROC_INIT: + gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); + default: + if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) + osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_GPRS_DETACH, NULL); + else if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu) { + osmo_fsm_inst_dispatch(mmctx->iu.mm_state_fsm, E_PMM_PS_DETACH, NULL); + mmctx->gb.llme = rat_chg->llme; + } + + mmctx->ran_type = rat_chg->new_ran_type; + break; + } + case E_GMM_CLEANUP: switch (fi->state) { case ST_GMM_DEREGISTERED: @@ -175,7 +236,7 @@ struct osmo_fsm gmm_fsm = { .states = gmm_fsm_states, .num_states = ARRAY_SIZE(gmm_fsm_states), .event_names = gmm_fsm_event_names, - .allstate_event_mask = X(E_GMM_CLEANUP), + .allstate_event_mask = X(E_GMM_CLEANUP) | X(E_GMM_RAT_CHANGE), .allstate_action = gmm_fsm_allstate_action, .log_subsys = DMM, .timer_cb = gmm_fsm_timer_cb, diff --git a/src/sgsn/gprs_llc.c b/src/sgsn/gprs_llc.c index 6e22e65c0..6f563851f 100644 --- a/src/sgsn/gprs_llc.c +++ b/src/sgsn/gprs_llc.c @@ -33,7 +33,7 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_llc.h> #include <osmocom/sgsn/crc24.h> @@ -42,16 +42,29 @@ #include <osmocom/sgsn/gprs_sndcp_comp.h> #include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/crypt/kdf.h> + const struct value_string gprs_llc_llme_state_names[] = { { GPRS_LLMS_UNASSIGNED, "UNASSIGNED" }, { GPRS_LLMS_ASSIGNED, "ASSIGNED" }, { 0, NULL } }; +const struct value_string gprs_llc_lle_state_names[] = { + { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, + { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, + { GPRS_LLES_LOCAL_EST, "Local Establishment" }, + { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, + { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, + { GPRS_LLES_LOCAL_REL, "Local Release" }, + { GPRS_LLES_TIMER_REC, "Timer Recovery" }, + { 0, NULL } +}; + static struct gprs_llc_llme *llme_alloc(uint32_t tlli); -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, +static int gprs_llc_tx_xid(const struct gprs_llc_lle *lle, struct msgb *msg, int command); -static int gprs_llc_tx_dm(struct gprs_llc_lle *lle); +static int gprs_llc_tx_dm(const struct gprs_llc_lle *lle); static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, enum gprs_llc_u_cmd u_cmd, int pf_bit); @@ -210,7 +223,7 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request, int bytes_request_len, uint8_t *bytes_response, int bytes_response_maxlen, - struct gprs_llc_lle *lle) + const struct gprs_llc_lle *lle) { /* Note: This function computes the response that is sent back to the * MS when a mobile originated XID is received. The function is @@ -286,7 +299,7 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request, /* Dispatch XID indications and responses comming from the MS */ static void rx_llc_xid(struct gprs_llc_lle *lle, - struct gprs_llc_hdr_parsed *gph) + const struct gprs_llc_hdr_parsed *gph) { uint8_t response[1024]; int response_len; @@ -376,20 +389,24 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) * not yet have a MMC context (e.g. XID negotiation of primarly * LLC connection from GMM sapi). */ if (mmctx) { + /* In rare cases the LLME is NULL in those cases don't + * use the mm radio capabilities */ dup.imsi = mmctx->imsi; - dup.drx_parms = mmctx->drx_parms; - dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; - dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; - - /* make sure we only send it to the right llme */ - if (!(msgb_tlli(msg) == mmctx->gb.llme->tlli - || msgb_tlli(msg) == mmctx->gb.llme->old_tlli)) { - LOGP(DLLC, LOGL_ERROR, - "_bssgp_tx_dl_ud(): Attempt to send Downlink Unitdata to wrong LLME:" - " msgb_tlli=0x%x mmctx->gb.llme->tlli=0x%x ->old_tlli=0x%x\n", - msgb_tlli(msg), mmctx->gb.llme->tlli, mmctx->gb.llme->old_tlli); - msgb_free(msg); - return -EINVAL; + if (mmctx->gb.llme) { + dup.drx_parms = mmctx->drx_parms; + dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; + dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; + + /* make sure we only send it to the right llme */ + if (!(msgb_tlli(msg) == mmctx->gb.llme->tlli + || msgb_tlli(msg) == mmctx->gb.llme->old_tlli)) { + LOGP(DLLC, LOGL_ERROR, + "_bssgp_tx_dl_ud(): Attempt to send Downlink Unitdata to wrong LLME:" + " msgb_tlli=0x%x mmctx->gb.llme->tlli=0x%x ->old_tlli=0x%x\n", + msgb_tlli(msg), mmctx->gb.llme->tlli, mmctx->gb.llme->old_tlli); + msgb_free(msg); + return -EINVAL; + } } } memcpy(&dup.qos_profile, qos_profile_default, @@ -584,6 +601,7 @@ static struct gprs_llc_llme *llme_alloc(uint32_t tlli) static void llme_free(struct gprs_llc_llme *llme) { + gprs_sndcp_sm_deactivate_ind_by_llme(llme); gprs_sndcp_comp_free(llme->comp.proto); gprs_sndcp_comp_free(llme->comp.data); llist_del(&llme->list); @@ -666,15 +684,15 @@ int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, /* Identifiers passed down: (BVCI, NSEI) */ - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_BYTES), msg->len); /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, NULL); } /* Send XID response to LLE */ -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, +static int gprs_llc_tx_xid(const struct gprs_llc_lle *lle, struct msgb *msg, int command) { /* copy identifiers from LLE to ensure lower layers can route */ @@ -685,7 +703,7 @@ static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); } -static int gprs_llc_tx_dm(struct gprs_llc_lle *lle) +static int gprs_llc_tx_dm(const struct gprs_llc_lle *lle) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_DM"); @@ -698,7 +716,7 @@ static int gprs_llc_tx_dm(struct gprs_llc_lle *lle) } /* encrypt information field + FCS, if needed! */ -static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, +static int apply_gea(const struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data) { uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; @@ -810,8 +828,8 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, } } - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_BYTES), msg->len); /* Identifiers passed down: (BVCI, NSEI) */ @@ -958,9 +976,9 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) llhp.data); if (rc < 0) return rc; - llhp.fcs = *(llhp.data + llhp.data_len); - llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; - llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; + llhp.fcs = *(llhp.data + llhp.data_len); + llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; + llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; } else { LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " "has no KC/Algo! Dropping.\n"); @@ -996,8 +1014,8 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) msgb_l3trim(msg, llhp.data_len); } - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_UL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_UL_BYTES), msg->len); /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) { @@ -1012,7 +1030,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ - rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); + rc = sndcp_ll_unitdata_ind(msg, lle, llhp.data, llhp.data_len); break; case GPRS_SAPI_SMS: /* FIXME */ @@ -1030,7 +1048,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) } /* Propagate crypto parameters MM -> LLME */ -void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) +void gprs_llme_copy_key(const struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) { if (!mm) return; @@ -1038,8 +1056,13 @@ void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) llme->algo = mm->ciph_algo; if (llme->cksn != mm->auth_triplet.key_seq && mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { - memcpy(llme->kc, mm->auth_triplet.vec.kc, - gprs_cipher_key_length(mm->ciph_algo)); + + /* gea4 needs kc128 */ + if (mm->ciph_algo == GPRS_ALGO_GEA4) + osmo_kdf_kc128(mm->auth_triplet.vec.ck, mm->auth_triplet.vec.ik, llme->kc); + else + memcpy(llme->kc, mm->auth_triplet.vec.kc, gprs_cipher_key_length(mm->ciph_algo)); + llme->cksn = mm->auth_triplet.key_seq; } } else diff --git a/src/sgsn/gprs_llc_vty.c b/src/sgsn/gprs_llc_vty.c index 418be8348..4572f957c 100644 --- a/src/sgsn/gprs_llc_vty.c +++ b/src/sgsn/gprs_llc_vty.c @@ -39,22 +39,11 @@ #include <osmocom/vty/vty.h> #include <osmocom/vty/command.h> -struct value_string gprs_llc_state_strs[] = { - { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, - { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, - { GPRS_LLES_LOCAL_EST, "Local Establishment" }, - { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, - { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, - { GPRS_LLES_LOCAL_REL, "Local Release" }, - { GPRS_LLES_TIMER_REC, "Timer Recovery" }, - { 0, NULL } -}; - static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle) { struct gprs_llc_params *par = &lle->params; - vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, - get_value_string(gprs_llc_state_strs, lle->state), + vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, + get_value_string(gprs_llc_lle_state_names, lle->state), lle->vu_send, lle->vu_recv); vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s", lle->v_sent, lle->v_ack, lle->v_recv, @@ -79,7 +68,7 @@ static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui, llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 : (int)(now_tp.tv_sec - (time_t)llme->age_timestamp), - get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE); + get_value_string(gprs_llc_llme_state_names, llme->state), VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) { struct gprs_llc_lle *lle; diff --git a/src/sgsn/gprs_mm_state_gb_fsm.c b/src/sgsn/gprs_mm_state_gb_fsm.c index 811f0c2f4..dde7b770a 100644 --- a/src/sgsn/gprs_mm_state_gb_fsm.c +++ b/src/sgsn/gprs_mm_state_gb_fsm.c @@ -1,3 +1,25 @@ +/* TS 23.060 § 6.1.1 Mobility Management States (A/Gb mode) */ +/* + * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ #include <osmocom/core/tdef.h> #include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> @@ -5,6 +27,7 @@ #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/mmctx.h> #define X(s) (1 << (s)) @@ -49,7 +72,7 @@ static void st_mm_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data) case E_MM_READY_TIMER_EXPIRY: mm_state_gb_fsm_state_chg(fi, ST_MM_STANDBY); break; - case E_MM_IMPLICIT_DETACH: + case E_MM_GPRS_DETACH: mm_state_gb_fsm_state_chg(fi, ST_MM_IDLE); break; case E_MM_PDU_RECEPTION: @@ -68,6 +91,9 @@ static void st_mm_standby(struct osmo_fsm_inst *fi, uint32_t event, void *data) case E_MM_PDU_RECEPTION: mm_state_gb_fsm_state_chg(fi, ST_MM_READY); break; + case E_MM_GPRS_DETACH: + mm_state_gb_fsm_state_chg(fi, ST_MM_IDLE); + break; } } @@ -80,13 +106,13 @@ static struct osmo_fsm_state mm_state_gb_fsm_states[] = { .action = st_mm_idle, }, [ST_MM_READY] = { - .in_event_mask = X(E_MM_READY_TIMER_EXPIRY) | X(E_MM_RA_UPDATE) | X(E_MM_IMPLICIT_DETACH) | X(E_MM_PDU_RECEPTION), + .in_event_mask = X(E_MM_READY_TIMER_EXPIRY) | X(E_MM_RA_UPDATE) | X(E_MM_GPRS_DETACH) | X(E_MM_PDU_RECEPTION), .out_state_mask = X(ST_MM_IDLE) | X(ST_MM_STANDBY), .name = "Ready", .action = st_mm_ready, }, [ST_MM_STANDBY] = { - .in_event_mask = X(E_MM_PDU_RECEPTION), + .in_event_mask = X(E_MM_PDU_RECEPTION) | X(E_MM_GPRS_DETACH), .out_state_mask = X(ST_MM_IDLE) | X(ST_MM_READY), .name = "Standby", .action = st_mm_standby, @@ -96,7 +122,7 @@ static struct osmo_fsm_state mm_state_gb_fsm_states[] = { const struct value_string mm_state_gb_fsm_event_names[] = { OSMO_VALUE_STRING(E_MM_GPRS_ATTACH), OSMO_VALUE_STRING(E_MM_PDU_RECEPTION), - OSMO_VALUE_STRING(E_MM_IMPLICIT_DETACH), + OSMO_VALUE_STRING(E_MM_GPRS_DETACH), OSMO_VALUE_STRING(E_MM_READY_TIMER_EXPIRY), OSMO_VALUE_STRING(E_MM_RA_UPDATE), { 0, NULL } diff --git a/src/sgsn/gprs_mm_state_iu_fsm.c b/src/sgsn/gprs_mm_state_iu_fsm.c index e571026c8..c2e9c4498 100644 --- a/src/sgsn/gprs_mm_state_iu_fsm.c +++ b/src/sgsn/gprs_mm_state_iu_fsm.c @@ -1,3 +1,25 @@ +/* TS 23.060 § 6.1.2 Mobility Management States (Iu mode) */ +/* + * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ #include <arpa/inet.h> #include <osmocom/core/tdef.h> @@ -7,13 +29,15 @@ #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/gprs_ranap.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> +#include <osmocom/sgsn/mmctx.h> #define X(s) (1 << (s)) static const struct osmo_tdef_state_timeout mm_state_iu_fsm_timeouts[32] = { [ST_PMM_DETACHED] = { }, - /* non-spec -T3314 (User inactivity timer) */ - [ST_PMM_CONNECTED] = { .T=-3314 }, + [ST_PMM_CONNECTED] = { }, [ST_PMM_IDLE] = { }, }; @@ -40,7 +64,7 @@ static void st_pmm_detached(struct osmo_fsm_inst *fi, uint32_t event, void *data case E_PMM_PS_ATTACH: mm_state_iu_fsm_state_chg(fi, ST_PMM_CONNECTED); break; - case E_PMM_IMPLICIT_DETACH: + case E_PMM_PS_DETACH: break; } } @@ -48,24 +72,16 @@ static void st_pmm_detached(struct osmo_fsm_inst *fi, uint32_t event, void *data static void st_pmm_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct sgsn_mm_ctx *ctx = fi->priv; - const struct RANAP_Cause user_inactive_cause = { - .present = RANAP_Cause_PR_radioNetwork, - .choice.radioNetwork = RANAP_CauseRadioNetwork_user_inactivity, - }; switch(event) { case E_PMM_PS_CONN_RELEASE: sgsn_ranap_iu_free(ctx); mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE); break; - case E_PMM_IMPLICIT_DETACH: + case E_PMM_PS_DETACH: sgsn_ranap_iu_release_free(ctx, NULL); mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED); break; - case E_PMM_USER_INACTIVITY: - sgsn_ranap_iu_release_free(ctx, &user_inactive_cause); - mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED); - break; case E_PMM_RA_UPDATE: break; } @@ -81,43 +97,37 @@ static void st_pmm_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch(event) { + case E_PMM_PS_ATTACH: case E_PMM_PS_CONN_ESTABLISH: mm_state_iu_fsm_state_chg(fi, ST_PMM_CONNECTED); break; - case E_PMM_IMPLICIT_DETACH: + case E_PMM_PS_DETACH: mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED); break; } } -static int pmm_state_fsm_timer_cb(struct osmo_fsm_inst *fi) -{ - switch(fi->state) { - case ST_PMM_CONNECTED: - /* timer for pmm state. state=CONNECTED: -T3314 (User inactivity timer) */ - osmo_fsm_inst_dispatch(fi, E_PMM_USER_INACTIVITY, NULL); - break; - } - - return 0; -} - static struct osmo_fsm_state mm_state_iu_fsm_states[] = { [ST_PMM_DETACHED] = { - .in_event_mask = X(E_PMM_PS_ATTACH) | X(E_PMM_IMPLICIT_DETACH), + .in_event_mask = X(E_PMM_PS_ATTACH) | X(E_PMM_PS_DETACH), .out_state_mask = X(ST_PMM_CONNECTED), .name = "Detached", .action = st_pmm_detached, }, [ST_PMM_CONNECTED] = { - .in_event_mask = X(E_PMM_PS_CONN_RELEASE) | X(E_PMM_RA_UPDATE) - | X(E_PMM_IMPLICIT_DETACH) | X(E_PMM_USER_INACTIVITY), + .in_event_mask = + X(E_PMM_PS_CONN_RELEASE) | + X(E_PMM_RA_UPDATE) | + X(E_PMM_PS_DETACH), .out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_IDLE), .name = "Connected", .action = st_pmm_connected, }, [ST_PMM_IDLE] = { - .in_event_mask = X(E_PMM_IMPLICIT_DETACH) | X(E_PMM_PS_CONN_ESTABLISH), + .in_event_mask = + X(E_PMM_PS_DETACH) | + X(E_PMM_PS_CONN_ESTABLISH) | + X(E_PMM_PS_ATTACH), .out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_CONNECTED), .name = "Idle", .onenter = st_pmm_idle_on_enter, @@ -129,9 +139,8 @@ const struct value_string mm_state_iu_fsm_event_names[] = { OSMO_VALUE_STRING(E_PMM_PS_ATTACH), OSMO_VALUE_STRING(E_PMM_PS_CONN_RELEASE), OSMO_VALUE_STRING(E_PMM_PS_CONN_ESTABLISH), - OSMO_VALUE_STRING(E_PMM_IMPLICIT_DETACH), + OSMO_VALUE_STRING(E_PMM_PS_DETACH), OSMO_VALUE_STRING(E_PMM_RA_UPDATE), - OSMO_VALUE_STRING(E_PMM_USER_INACTIVITY), { 0, NULL } }; @@ -140,7 +149,6 @@ struct osmo_fsm mm_state_iu_fsm = { .states = mm_state_iu_fsm_states, .num_states = ARRAY_SIZE(mm_state_iu_fsm_states), .event_names = mm_state_iu_fsm_event_names, - .timer_cb = pmm_state_fsm_timer_cb, .log_subsys = DMM, }; diff --git a/src/sgsn/gprs_ns.c b/src/sgsn/gprs_ns.c new file mode 100644 index 000000000..eb447facd --- /dev/null +++ b/src/sgsn/gprs_ns.c @@ -0,0 +1,103 @@ +/* Messages on the Gb interface (A/Gb mode) */ + +/* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/core/rate_ctr.h> + +#include <osmocom/gprs/gprs_msgb.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_ns2.h> +#include <osmocom/gprs/gprs_bssgp_bss.h> +#include <osmocom/sgsn/gprs_llc.h> + +#include "config.h" + +#include <osmocom/sgsn/debug.h> + +void gprs_ns_prim_status_cb(struct osmo_gprs_ns2_prim *nsp) +{ + switch (nsp->u.status.cause) { + case GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED: + LOGP(DGPRS, LOGL_NOTICE, "NS-E %d SNS configured.\n", nsp->nsei); + break; + case GPRS_NS2_AFF_CAUSE_RECOVERY: + LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became available\n", nsp->nsei); + /* workaround for broken BSS which doesn't respond correct to BSSGP status message. + * Sent a BSSGP Reset when a persistent NSVC comes up for the first time. */ + if (nsp->u.status.first && nsp->u.status.persistent) { + struct bssgp_bvc_ctx bctx = { + .nsei = nsp->nsei, + }; + bssgp_tx_bvc_reset2(&bctx, BVCI_SIGNALLING, BSSGP_CAUSE_EQUIP_FAIL, false); + } + break; + case GPRS_NS2_AFF_CAUSE_FAILURE: + LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became unavailable\n", nsp->nsei); + break; + default: + LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n", + get_value_string(osmo_prim_op_names, nsp->oph.operation), nsp->oph.primitive); + break; + } +} + +/* call-back function for the NS protocol */ +int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + struct osmo_gprs_ns2_prim *nsp; + int rc = 0; + + if (oph->sap != SAP_NS) + return 0; + + nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph); + + if (oph->operation != PRIM_OP_INDICATION) { + LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n", + get_value_string(osmo_prim_op_names, oph->operation), + oph->operation); + return 0; + } + + switch (oph->primitive) { + case GPRS_NS2_PRIM_UNIT_DATA: + /* hand the message into the BSSGP implementation */ + /* add required msg fields for Gb layer */ + msgb_bssgph(oph->msg) = oph->msg->l3h; + msgb_bvci(oph->msg) = nsp->bvci; + msgb_nsei(oph->msg) = nsp->nsei; + rc = bssgp_rcvmsg(oph->msg); + break; + case GPRS_NS2_PRIM_STATUS: + gprs_ns_prim_status_cb(nsp); + break; + default: + LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n", + get_value_string(osmo_prim_op_names, oph->operation), oph->primitive); + break; + } + + if (oph->msg) + msgb_free(oph->msg); + + return rc; +} diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c index 7a3349192..5e0d8edc6 100644 --- a/src/sgsn/gprs_ranap.c +++ b/src/sgsn/gprs_ranap.c @@ -21,11 +21,12 @@ * */ -#include "bscconfig.h" +#include "config.h" #include <gtp.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/tdef.h> +#include <osmocom/gprs/gprs_msgb.h> #include <osmocom/ranap/ranap_common.h> @@ -36,6 +37,10 @@ #include <osmocom/sgsn/gprs_ranap.h> #include <osmocom/sgsn/gprs_gmm_attach.h> #include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> +#include <osmocom/sgsn/mmctx.h> /* Send RAB activation requests for all PDP contexts */ void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) @@ -119,7 +124,8 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type mm = sgsn_mm_ctx_by_ue_ctx(ctx); if (!mm) { - LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %d\n", type); + LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %s\n", + ranap_iu_event_type_str(type)); ranap_iu_free_ue(ctx); return rc; } @@ -132,10 +138,9 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type /* fall thru */ case RANAP_IU_EVENT_LINK_INVALIDATED: /* Clean up ranap_ue_conn_ctx here */ - LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi); - if (mm->iu.mm_state_fsm->state == ST_PMM_CONNECTED) - osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL); - else + LOGMMCTXP(LOGL_INFO, mm, "IU release (cause=%s)\n", ranap_iu_event_type_str(type)); + rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL); + if (rc < 0) sgsn_ranap_iu_free(mm); /* TODO: move this into FSM */ @@ -144,6 +149,11 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type rc = 0; break; case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE: + /* FIXME: verify that a permitted UEA level was chosen. Compare how osmo-msc does it in + * msc_a_ran_dec_from_msc_i(), case RAN_MSG_CIPHER_MODE_COMPLETE. + * We should dissolve iu_client.c, it was a design mistake when first implementing Iu support. osmo-msc + * has moved away from it a long time ago. + */ /* Continue authentication here */ mm->iu.ue_ctx->integrity_active = 1; ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi); @@ -231,7 +241,7 @@ int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg)); if (mmctx) { - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN)); if (ra_id) memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra)); } diff --git a/src/sgsn/gprs_sm.c b/src/sgsn/gprs_sm.c index f8019ab6a..88d1feb5a 100644 --- a/src/sgsn/gprs_sm.c +++ b/src/sgsn/gprs_sm.c @@ -26,7 +26,7 @@ #include <arpa/inet.h> #include <netdb.h> -#include "bscconfig.h" +#include "config.h" #include <osmocom/core/rate_ctr.h> #include <osmocom/core/tdef.h> @@ -36,13 +36,15 @@ #include <osmocom/sgsn/gprs_sm.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gtp_ggsn.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/gprs_llc.h> #include <osmocom/sgsn/gprs_sndcp.h> #include <osmocom/sgsn/gprs_ranap.h> - -extern void *tall_sgsn_ctx; +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> +#include <osmocom/sgsn/mmctx.h> /* 3GPP TS 04.08 sec 6.1.3.4.3(.a) "Abnormal cases" */ #define T339X_MAX_RETRANS 4 @@ -186,7 +188,7 @@ int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ LOGPDPCTXP(LOGL_INFO, pdp, "<- ACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_ACCEPT]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_ACTIVATE_ACCEPT)); mmctx2msgid(msg, pdp->mm); @@ -232,7 +234,7 @@ int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, LOGMMCTXP(LOGL_NOTICE, mm, "<- ACTIVATE PDP CONTEXT REJ: %s\n", get_value_string(gsm48_gsm_cause_names, cause)); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REJECT]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_ACTIVATE_REJECT)); mmctx2msgid(msg, mm); @@ -257,7 +259,7 @@ static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, uint8_t tear_down_ind = (0x9 << 4) | (!!teardown); LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT REQ\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_DL_DEACTIVATE_REQUEST)); mmctx2msgid(msg, mm); @@ -285,7 +287,7 @@ static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_ACCEPT]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_DL_DEACTIVATE_ACCEPT)); mmctx2msgid(msg, mm); @@ -376,7 +378,7 @@ static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent * goto reject_due_failure; } - ggsn = sgsn_ggsn_ctx_alloc(UINT32_MAX); + ggsn = sgsn_ggsn_ctx_alloc(sgsn, UINT32_MAX); if (!ggsn) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Failed to create ggsn.\n"); goto reject_due_failure; @@ -448,7 +450,7 @@ static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, bool *del DEBUGPC(DMM, "IPv4 "); if (req_pdpa_len >= 6) { struct in_addr ia; - ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2))); + ia.s_addr = osmo_load32be(req_pdpa+2); DEBUGPC(DMM, "%s ", inet_ntop(AF_INET, &ia, buf, sizeof(buf))); } break; @@ -533,7 +535,7 @@ static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, bool *del /* Only increment counter for a real activation, after we checked * for re-transmissions */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PDP_CTX_ACT)); /* Determine GGSN based on APN and subscription options */ ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause, apn_str); @@ -591,7 +593,7 @@ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg; int rc; - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_ACTIVATE_REQUEST)); /* * This is painful. We might not have a static GGSN @@ -629,7 +631,7 @@ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", get_value_string(gsm48_gsm_cause_names, gh->data[0])); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_REQUEST]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_UL_DEACTIVATE_REQUEST)); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { @@ -639,7 +641,11 @@ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); } - return sgsn_delete_pdp_ctx(pdp); + if (pdp->ggsn) + return sgsn_delete_pdp_ctx(pdp); + /* GTP side already detached, freeing */ + sgsn_pdp_ctx_free(pdp); + return 0; } /* 3GPP TS 24.008 § 9.5.9: Deactivate PDP Context Accept */ @@ -650,7 +656,7 @@ static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_ACCEPT]); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_PDP_UL_DEACTIVATE_ACCEPT)); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { diff --git a/src/sgsn/gprs_sndcp.c b/src/sgsn/gprs_sndcp.c index 01be57ef2..3eae127fc 100644 --- a/src/sgsn/gprs_sndcp.c +++ b/src/sgsn/gprs_sndcp.c @@ -28,10 +28,11 @@ #include <osmocom/core/linuxlist.h> #include <osmocom/core/timer.h> #include <osmocom/core/talloc.h> +#include <osmocom/core/endian.h> #include <osmocom/gprs/gprs_bssgp.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_gb.h> +#include <osmocom/sgsn/gprs_ns.h> #include <osmocom/sgsn/gprs_llc.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/gprs_sndcp.h> @@ -40,6 +41,9 @@ #include <osmocom/sgsn/gprs_sndcp_pcomp.h> #include <osmocom/sgsn/gprs_sndcp_dcomp.h> #include <osmocom/sgsn/gprs_sndcp_comp.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/mmctx.h> +#include <osmocom/sgsn/gtp.h> #define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */ @@ -65,7 +69,7 @@ static uint16_t calc_ip_csum(uint8_t *data, int len) } /* Calculate TCP/IP checksum */ -static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) +static uint16_t calc_tcpip_csum(const void *ctx, const uint8_t *packet, int len) { uint8_t *buf; uint16_t csum; @@ -83,7 +87,7 @@ static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) } /* Show some ip packet details */ -static void debug_ip_packet(uint8_t *data, int len, int dir, char *info) +static void debug_ip_packet(const uint8_t *data, int len, int dir, const char *info) { uint8_t tcp_flags; char flags_debugmsg[256]; @@ -164,27 +168,43 @@ static void debug_ip_packet(uint8_t *data, int len, int dir, char *info) /* Chapter 7.2: SN-PDU Formats */ struct sndcp_common_hdr { +#if OSMO_IS_LITTLE_ENDIAN /* octet 1 */ uint8_t nsapi:4; uint8_t more:1; uint8_t type:1; uint8_t first:1; uint8_t spare:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t spare:1, first:1, type:1, more:1, nsapi:4; +#endif } __attribute__((packed)); /* PCOMP / DCOMP only exist in first fragment */ struct sndcp_comp_hdr { +#if OSMO_IS_LITTLE_ENDIAN /* octet 2 */ uint8_t pcomp:4; uint8_t dcomp:4; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t dcomp:4, pcomp:4; +#endif } __attribute__((packed)); struct sndcp_udata_hdr { +#if OSMO_IS_LITTLE_ENDIAN /* octet 3 */ uint8_t npdu_high:4; uint8_t seg_nr:4; /* octet 4 */ uint8_t npdu_low; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t seg_nr:4, npdu_high:4; + uint8_t npdu_low; +#endif } __attribute__((packed)); @@ -204,7 +224,8 @@ struct defrag_queue_entry { LLIST_HEAD(gprs_sndcp_entities); /* Check if any compression parameters are set in the sgsn configuration */ -static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) { +static inline int any_pcomp_or_dcomp_active(const struct sgsn_instance *sgsn) +{ if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive || sgsn->cfg.dcomp_v42bis.active || sgsn->cfg.dcomp_v42bis.passive) return true; @@ -243,7 +264,7 @@ static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, } /* return if we have all segments of this N-PDU */ -static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) +static int defrag_have_all_segments(const struct gprs_sndcp_entity *sne) { uint32_t seg_needed = 0; unsigned int i; @@ -258,7 +279,7 @@ static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) return 0; } -static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, +static struct defrag_queue_entry *defrag_get_seg(const struct gprs_sndcp_entity *sne, uint32_t seg_nr) { struct defrag_queue_entry *dqe; @@ -272,13 +293,62 @@ static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, return NULL; } +/* Returns talloced buffer containing decompressed data, NULL on error. */ +static uint8_t *decompress_segment(struct gprs_sndcp_entity *sne, void *ctx, + const uint8_t *compressed_data, unsigned int compressed_data_len, + unsigned int *decompressed_data_len) +{ + int rc; + uint8_t *expnd = NULL; + *decompressed_data_len = 0; + +#if DEBUG_IP_PACKETS == 1 + DEBUGP(DSNDCP, "\n"); + DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); + DEBUGP(DSNDCP, "===================================================\n"); +#endif + + expnd = talloc_zero_size(ctx, compressed_data_len * MAX_DATADECOMPR_FAC + + MAX_HDRDECOMPR_INCR); + memcpy(expnd, compressed_data, compressed_data_len); + + /* Apply data decompression */ + rc = gprs_sndcp_dcomp_expand(expnd, compressed_data_len, sne->defrag.dcomp, + sne->defrag.data); + if (rc < 0) { + LOGP(DSNDCP, LOGL_ERROR, + "Data decompression failed!\n"); + talloc_free(expnd); + return NULL; + } + + /* Apply header decompression */ + rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, sne->defrag.proto); + if (rc < 0) { + LOGP(DSNDCP, LOGL_ERROR, + "TCP/IP Header decompression failed!\n"); + talloc_free(expnd); + return NULL; + } + + *decompressed_data_len = rc; + +#if DEBUG_IP_PACKETS == 1 + debug_ip_packet(expnd, *decompressed_data_len, 1, "defrag_segments()"); + DEBUGP(DSNDCP, "===================================================\n"); + DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); + DEBUGP(DSNDCP, "\n"); +#endif + return expnd; +} + /* Perform actual defragmentation and create an output packet */ static int defrag_segments(struct gprs_sndcp_entity *sne) { struct msgb *msg; unsigned int seg_nr; uint8_t *npdu; - int npdu_len; + unsigned int npdu_len; int rc; uint8_t *expnd = NULL; @@ -319,55 +389,26 @@ static int defrag_segments(struct gprs_sndcp_entity *sne) * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ /* Decompress packet */ -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, " \n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, "===================================================\n"); -#endif if (any_pcomp_or_dcomp_active(sgsn)) { - - expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + - MAX_HDRDECOMPR_INCR); - memcpy(expnd, npdu, npdu_len); - - /* Apply data decompression */ - rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, - sne->defrag.data); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Apply header decompression */ - rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, - sne->defrag.proto); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "TCP/IP Header decompression failed!\n"); - talloc_free(expnd); - return -EIO; + expnd = decompress_segment(sne, msg, npdu, npdu_len, &npdu_len); + if (!expnd) { + rc = -EIO; + goto ret_free; } - - /* Modify npu length, expnd is handed directly handed - * over to gsn_rx_sndcp_ud_ind(), see below */ - npdu_len = rc; - } else + } else { expnd = npdu; -#if DEBUG_IP_PACKETS == 1 - debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()"); - DEBUGP(DSNDCP, "===================================================\n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, " \n"); -#endif + } - /* Hand off packet to gtp */ - rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, - sne->nsapi, msg, npdu_len, expnd); + /* Hand off packet to SGSN (SNDCP SN-UNITDATA.ind), which will forward it to GGSN (GTP): */ + rc = sndcp_sn_unitdata_ind(sne, msg, npdu_len, expnd); - if (any_pcomp_or_dcomp_active(sgsn)) - talloc_free(expnd); +ret_free: + /* we must free the memory we allocated above; ownership is not transferred + * downwards in the call above */ + msgb_free(msg); + + /* Note: We do not have to free expnd explicitly, because it is created + * within the talloc context of msg, which we just freed. */ return rc; } @@ -501,7 +542,7 @@ int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) } /* Entry point for the SNSM-DEACTIVATE.indication */ -int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) +int sndcp_sm_deactivate_ind(const struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; @@ -523,6 +564,20 @@ int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) return 0; } +/* Clean up all gprs_sndcp_entities related to llme (OS#4824) */ +void gprs_sndcp_sm_deactivate_ind_by_llme(const struct gprs_llc_llme *llme) +{ + struct gprs_sndcp_entity *sne, *sne2; + + llist_for_each_entry_safe(sne, sne2, &gprs_sndcp_entities, list) { + if (sne->lle->llme == llme) { + LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind for SNDCP attached to llme=%p\n", llme); + /* Free and remove from list */ + sndcp_sm_deactivate_ind(sne->lle, sne->nsapi); + } + } +} + /* Fragmenter state */ struct sndcp_frag_state { uint8_t frag_nr; @@ -630,7 +685,7 @@ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs, } /* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ -int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, +int sndcp_sn_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, void *mmcontext) { struct gprs_sndcp_entity *sne; @@ -688,13 +743,14 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi sne = gprs_sndcp_entity_by_lle(lle, nsapi); if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); + LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity (lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", + lle, lle->llme->tlli, lle->sapi, nsapi); msgb_free(msg); return -EIO; } /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ - if (msg->len > lle->params.n201_u - + if (msg->len > lle->params.n201_u - (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { /* initialize the fragmenter state */ fs.msg = msg; @@ -739,7 +795,7 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi } /* Section 5.1.2.17 LL-UNITDATA.ind */ -int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, +int sndcp_ll_unitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint16_t len) { struct gprs_sndcp_entity *sne; @@ -787,7 +843,7 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, lle, lle->llme->tlli, lle->sapi, sch->nsapi); return -EIO; } - gprs_gb_recv_pdu(mmctx); + gprs_gb_recv_pdu(mmctx, msg); if (scomph) { sne->defrag.pcomp = scomph->pcomp; @@ -809,63 +865,40 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); return -EIO; } - /* actually send the N-PDU to the SGSN core code, which then - * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ + /* actually send the N-PDU to the SGSN core code (SNDCP SN-UNITDATA.ind) */ /* Decompress packet */ -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, " \n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, "===================================================\n"); -#endif if (any_pcomp_or_dcomp_active(sgsn)) { - - expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + - MAX_HDRDECOMPR_INCR); - memcpy(expnd, npdu, npdu_len); - - /* Apply data decompression */ - rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, - sne->defrag.data); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data decompression failed!\n"); - talloc_free(expnd); - return -EIO; + expnd = decompress_segment(sne, msg, npdu, npdu_len, (unsigned int *)&npdu_len); + if (!expnd) { + rc = -EIO; + goto ret_free; } - - /* Apply header decompression */ - rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, - sne->defrag.proto); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "TCP/IP Header decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Modify npu length, expnd is handed directly handed - * over to gsn_rx_sndcp_ud_ind(), see below */ - npdu_len = rc; - } else + } else { expnd = npdu; -#if DEBUG_IP_PACKETS == 1 - debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()"); - DEBUGP(DSNDCP, "===================================================\n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, " \n"); -#endif + } /* Hand off packet to gtp */ - rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, - sne->nsapi, msg, npdu_len, expnd); + rc = sndcp_sn_unitdata_ind(sne, msg, npdu_len, expnd); +ret_free: if (any_pcomp_or_dcomp_active(sgsn)) talloc_free(expnd); return rc; } +/* 5.1.1.4 SN-UNITDATA.indication + * Called by SNDCP when it has received/re-assembled a N-PDU + */ +int sndcp_sn_unitdata_ind(struct gprs_sndcp_entity *sne, + struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) +{ + /* Hand it off N-PDU to the correct GTP tunnel + GGSN: */ + return sgsn_gtp_data_req(&sne->ra_id, sne->lle->llme->tlli, + sne->nsapi, msg, npdu_len, npdu); +} + #if 0 /* Section 5.1.2.1 LL-RESET.ind */ static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) @@ -1021,7 +1054,7 @@ int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi) /* Handle header compression entites */ static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field, - struct gprs_llc_lle *lle) + const struct gprs_llc_lle *lle) { /* Note: This functions also transforms the comp_field into its * echo form (strips comp values, resets propose bit etc...) @@ -1071,7 +1104,7 @@ static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field, /* Hanle data compression entites */ static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field, - struct gprs_llc_lle *lle) + const struct gprs_llc_lle *lle) { /* See note in handle_pcomp_entities() */ @@ -1113,7 +1146,7 @@ static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field, * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication, struct gprs_llc_xid_field *xid_field_response, - struct gprs_llc_lle *lle) + const struct gprs_llc_lle *lle) { /* Note: This function computes the SNDCP-XID response that is sent * back to the ms when a ms originated XID is received. The diff --git a/src/sgsn/gprs_subscriber.c b/src/sgsn/gprs_subscriber.c index 484c7ef4e..a52abe8f1 100644 --- a/src/sgsn/gprs_subscriber.c +++ b/src/sgsn/gprs_subscriber.c @@ -30,7 +30,7 @@ #include <osmocom/gsupclient/gsup_client.h> #include <osmocom/sgsn/sgsn.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_utils.h> @@ -48,8 +48,6 @@ (gsup)->imsi, \ ## args) -extern void *tall_sgsn_ctx; - LLIST_HEAD(_gprs_subscribers); struct llist_head * const gprs_subscribers = &_gprs_subscribers; @@ -378,7 +376,8 @@ static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr, } OSMO_ASSERT(pdp_data != NULL); - pdp_data->pdp_type = pdp_info->pdp_type; + pdp_data->pdp_type_org = pdp_info->pdp_type_org; + pdp_data->pdp_type_nr = pdp_info->pdp_type_nr; osmo_apn_to_str(pdp_data->apn_str, pdp_info->apn_enc, pdp_info->apn_enc_len); @@ -812,7 +811,7 @@ static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr, return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } -int gprs_subscr_location_update(struct gprs_subscr *subscr) +int gprs_subscr_location_update(struct gprs_subscr *subscr, enum sgsn_ran_type ran_type) { struct osmo_gsup_message gsup_msg = {0}; @@ -820,6 +819,18 @@ int gprs_subscr_location_update(struct gprs_subscr *subscr) "subscriber data is not available\n"); gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; + + switch (ran_type) { + case MM_CTX_T_GERAN_Gb: + gsup_msg.current_rat_type = OSMO_RAT_GERAN_A; + break; + case MM_CTX_T_UTRAN_Iu: + gsup_msg.current_rat_type = OSMO_RAT_UTRAN_IU; + break; + default: + break; + } + return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } @@ -858,6 +869,8 @@ struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx if (!subscr) { subscr = gprs_subscr_get_or_create(mmctx->imsi); + if (!subscr) + return NULL; subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; } @@ -881,10 +894,12 @@ int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n"); subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); + if (!subscr) + return -ENOMEM; subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; - rc = gprs_subscr_location_update(subscr); + rc = gprs_subscr_location_update(subscr, mmctx->ran_type); gprs_subscr_put(subscr); return rc; } @@ -906,6 +921,8 @@ int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n"); subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); + if (!subscr) + return -ENOMEM; subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; diff --git a/src/sgsn/gtp_ggsn.c b/src/sgsn/gtp_ggsn.c new file mode 100644 index 000000000..14cdfea00 --- /dev/null +++ b/src/sgsn/gtp_ggsn.c @@ -0,0 +1,178 @@ +/* GGSN context (peer) */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> + +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_gmm_fsm.h> +#include <osmocom/sgsn/gprs_sm.h> +#include <osmocom/sgsn/pdpctx.h> + +void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc) +{ + bool pending = osmo_timer_pending(&ggc->echo_timer); + + /* Only enable if allowed by policy and at least 1 pdp ctx exists against ggsn */ + if (!llist_empty(&ggc->pdp_list) && ggc->echo_interval) { + if (!pending) + osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); + } else { + if (pending) + osmo_timer_del(&ggc->echo_timer); + } +} + +/* GGSN contexts */ +static void echo_timer_cb(void *data) +{ + struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data; + sgsn_ggsn_echo_req(ggc); + osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); +} + +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(struct sgsn_instance *sgsn, uint32_t id) +{ + struct sgsn_ggsn_ctx *ggc; + + ggc = talloc_zero(sgsn, struct sgsn_ggsn_ctx); + if (!ggc) + return NULL; + + ggc->id = id; + ggc->gtp_version = 1; + ggc->remote_restart_ctr = -1; + /* if we are called from config file parse, this gsn doesn't exist yet */ + ggc->gsn = sgsn->gsn; + INIT_LLIST_HEAD(&ggc->pdp_list); + osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc); + llist_add(&ggc->list, &sgsn->ggsn_list); + + return ggc; +} + +void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) +{ + OSMO_ASSERT(llist_empty(&ggc->pdp_list)); + llist_del(&ggc->list); + talloc_free(ggc); +} + +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(struct sgsn_instance *sgsn, uint32_t id) +{ + struct sgsn_ggsn_ctx *ggc; + + llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { + if (id == ggc->id) + return ggc; + } + return NULL; +} + +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct sgsn_instance *sgsn, struct in_addr *addr) +{ + struct sgsn_ggsn_ctx *ggc; + + llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { + if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) + return ggc; + } + return NULL; +} + + +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(struct sgsn_instance *sgsn, uint32_t id) +{ + struct sgsn_ggsn_ctx *ggc; + + ggc = sgsn_ggsn_ctx_by_id(sgsn, id); + if (!ggc) + ggc = sgsn_ggsn_ctx_alloc(sgsn, id); + return ggc; +} + +void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx) +{ + /* the MM context can be deleted while the GGSN is not reachable or + * if has been crashed. */ + if (pctx->mm && pctx->mm->gmm_fsm->state == ST_GMM_REGISTERED_NORMAL) { + gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true); + sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx); + } else { + /* FIXME: GPRS paging in case MS is SUSPENDED */ + LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN " + "recovery\n"); + /* FIXME: how to tell this to libgtp? */ + sgsn_pdp_ctx_free(pctx); + } +} + +/* High-level function to be called in case a GGSN has disappeared or + * otherwise lost state (recovery procedure). It will detach all related pdp ctx + * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can + * be kept alive to allow handling later message which contained the Recovery IE. */ +int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except) +{ + int num = 0; + + struct sgsn_pdp_ctx *pdp, *pdp2; + llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) { + if (pdp == except) + continue; + sgsn_ggsn_ctx_drop_pdp(pdp); + num++; + } + + return num; +} + +int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn) +{ + return sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, NULL); +} + +void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + llist_add(&pdp->ggsn_list, &ggc->pdp_list); + sgsn_ggsn_ctx_check_echo_timer(ggc); +} + +void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + llist_del(&pdp->ggsn_list); + sgsn_ggsn_ctx_check_echo_timer(ggc); + if (pdp->destroy_ggsn) + sgsn_ggsn_ctx_free(pdp->ggsn); + pdp->ggsn = NULL; + /* Drop references to libgtp since the conn is down */ + if (pdp->lib) + pdp_freepdp(pdp->lib); + pdp->lib = NULL; +} diff --git a/src/sgsn/gtp_mme.c b/src/sgsn/gtp_mme.c new file mode 100644 index 000000000..3d7ec76b6 --- /dev/null +++ b/src/sgsn/gtp_mme.c @@ -0,0 +1,143 @@ +/* TS 29.060 § 7.5.14 RAN Information Management Messages */ +/* + * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <talloc.h> + +#include <osmocom/sgsn/gtp_mme.h> +#include <osmocom/sgsn/sgsn.h> + +static bool _eutran_tai_equal(const struct osmo_eutran_tai *t1, const struct osmo_eutran_tai *t2) +{ + return t1->mcc == t2->mcc && + t1->mnc == t2->mnc && + t1->mnc_3_digits == t2->mnc_3_digits && + t1->tac == t2->tac; +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_alloc(struct sgsn_instance *sgsn, const char *name) +{ + struct sgsn_mme_ctx *mme; + mme = talloc_zero(sgsn, struct sgsn_mme_ctx); + if (!mme) + return NULL; + + /* if we are called from config file parse, this gsn doesn't exist yet */ + mme->sgsn = sgsn; + + mme->name = talloc_strdup(mme, name); + + INIT_LLIST_HEAD(&mme->routes); + llist_add_tail(&mme->list, &sgsn->mme_list); + + return mme; +} + +void sgsn_mme_ctx_free(struct sgsn_mme_ctx *mme) +{ + struct mme_rim_route *rt, *rt2; + llist_del(&mme->list); + + llist_for_each_entry_safe(rt, rt2, &mme->routes, list) { + llist_del(&rt->list); + talloc_free(rt); + } + + talloc_free(mme); +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_find_alloc(struct sgsn_instance *sgsn, const char *name) +{ + struct sgsn_mme_ctx *mme; + + mme = sgsn_mme_ctx_by_name(sgsn, name); + if (!mme) + mme = sgsn_mme_ctx_alloc(sgsn, name); + return mme; +} + +void sgsn_mme_ctx_route_add(struct sgsn_mme_ctx *mme, const struct osmo_eutran_tai *tai) +{ + struct mme_rim_route *rt = talloc_zero(mme, struct mme_rim_route); + rt->tai = *tai; + llist_add_tail(&rt->list, &mme->routes); +} + +void sgsn_mme_ctx_route_del(struct sgsn_mme_ctx *mme, const struct osmo_eutran_tai *tai) +{ + struct mme_rim_route *rt; + + llist_for_each_entry(rt, &mme->routes, list) { + if (_eutran_tai_equal(tai, &rt->tai)) { + llist_del(&rt->list); + talloc_free(rt); + return; + } + } +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_by_name(const struct sgsn_instance *sgsn, const char *name) +{ + struct sgsn_mme_ctx *mme; + + llist_for_each_entry(mme, &sgsn->mme_list, list) { + if (!strcmp(name, mme->name)) + return mme; + } + return NULL; +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_by_addr(const struct sgsn_instance *sgsn, const struct in_addr *addr) +{ + struct sgsn_mme_ctx *mme; + + llist_for_each_entry(mme, &sgsn->mme_list, list) { + if (!memcmp(addr, &mme->remote_addr, sizeof(*addr))) + return mme; + } + return NULL; +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_by_route(const struct sgsn_instance *sgsn, const struct osmo_eutran_tai *tai) +{ + struct sgsn_mme_ctx *mme; + llist_for_each_entry(mme, &sgsn->mme_list, list) { + struct mme_rim_route *rt; + llist_for_each_entry(rt, &mme->routes, list) { + if (_eutran_tai_equal(tai, &rt->tai)) { + return mme; + } + } + } + return NULL; +} + +struct sgsn_mme_ctx *sgsn_mme_ctx_by_default_route(const struct sgsn_instance *sgsn) +{ + struct sgsn_mme_ctx *mme; + + llist_for_each_entry(mme, &sgsn->mme_list, list) { + if (mme->default_route) + return mme; + } + return NULL; +} diff --git a/src/sgsn/gprs_sgsn.c b/src/sgsn/mmctx.c index cb2c0fc25..0e9309284 100644 --- a/src/sgsn/gprs_sgsn.c +++ b/src/sgsn/mmctx.c @@ -1,4 +1,4 @@ -/* GPRS SGSN functionality */ +/* Mobility Management context */ /* (C) 2009 by Harald Welte <laforge@gnumonks.org> * @@ -27,7 +27,9 @@ #include <osmocom/core/rate_ctr.h> #include <osmocom/core/stats.h> #include <osmocom/core/backtrace.h> -#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/ports.h> +#include <osmocom/gprs/gprs_ns2.h> #include <osmocom/gprs/gprs_bssgp.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/gsm/apn.h> @@ -36,7 +38,7 @@ #include <osmocom/sgsn/gprs_subscriber.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_sm.h> @@ -47,22 +49,16 @@ #include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> #include <osmocom/sgsn/gprs_gmm_fsm.h> #include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> #include <pdp.h> #include <time.h> -#include "../../bscconfig.h" - -#define GPRS_LLME_CHECK_TICK 30 - -extern struct sgsn_instance *sgsn; -extern void *tall_sgsn_ctx; - -LLIST_HEAD(sgsn_mm_ctxts); -LLIST_HEAD(sgsn_ggsn_ctxts); -LLIST_HEAD(sgsn_apn_ctxts); -LLIST_HEAD(sgsn_pdp_ctxts); +#include "../../config.h" const struct value_string sgsn_ran_type_names[] = { { MM_CTX_T_GERAN_Gb, "GPRS/EDGE via Gb" }, @@ -95,66 +91,12 @@ static const struct rate_ctr_group_desc mmctx_ctrg_desc = { .class_id = OSMO_STATS_CLASS_SUBSCRIBER, }; -static const struct rate_ctr_desc pdpctx_ctr_description[] = { - { "udata:packets:in", "User Data Messages ( In)" }, - { "udata:packets:out", "User Data Messages (Out)" }, - { "udata:bytes:in", "User Data Bytes ( In)" }, - { "udata:bytes:out", "User Data Bytes (Out)" }, -}; - -static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { - .group_name_prefix = "sgsn:pdpctx", - .group_description = "SGSN PDP Context Statistics", - .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), - .ctr_desc = pdpctx_ctr_description, - .class_id = OSMO_STATS_CLASS_SUBSCRIBER, -}; - -static const struct rate_ctr_desc sgsn_ctr_description[] = { - { "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" }, - { "llc:ul_bytes", "Count successful received LLC bytes (encrypt & fcs correct)" }, - { "llc:dl_packets", "Count successful sent LLC packets before giving it to the bssgp layer" }, - { "llc:ul_packets", "Count successful received LLC packets (encrypt & fcs correct)" }, - { "gprs:attach_requested", "Received attach requests" }, - { "gprs:attach_accepted", "Sent attach accepts" }, - { "gprs:attach_rejected", "Sent attach rejects" }, - { "gprs:detach_requested", "Received detach requests" }, - { "gprs:detach_acked", "Sent detach acks" }, - { "gprs:routing_area_requested", "Received routing area requests" }, - { "gprs:routing_area_requested", "Sent routing area acks" }, - { "gprs:routing_area_requested", "Sent routing area rejects" }, - { "pdp:activate_requested", "Received activate requests" }, - { "pdp:activate_rejected", "Sent activate rejects" }, - { "pdp:activate_accepted", "Sent activate accepts" }, - { "pdp:request_activated", "unused" }, - { "pdp:request_activate_rejected", "unused" }, - { "pdp:modify_requested", "unused" }, - { "pdp:modify_accepted", "unused" }, - { "pdp:dl_deactivate_requested", "Sent deactivate requests" }, - { "pdp:dl_deactivate_accepted", "Sent deactivate accepted" }, - { "pdp:ul_deactivate_requested", "Received deactivate requests" }, - { "pdp:ul_deactivate_accepted", "Received deactivate accepts" }, -}; - -static const struct rate_ctr_group_desc sgsn_ctrg_desc = { - "sgsn", - "SGSN Overall Statistics", - OSMO_STATS_CLASS_GLOBAL, - ARRAY_SIZE(sgsn_ctr_description), - sgsn_ctr_description, -}; - -void sgsn_rate_ctr_init() { - sgsn->rate_ctrs = rate_ctr_group_alloc(tall_sgsn_ctx, &sgsn_ctrg_desc, 0); - OSMO_ASSERT(sgsn->rate_ctrs); -} - /* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/ struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx) { struct sgsn_mm_ctx *ctx; - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + llist_for_each_entry(ctx, &sgsn->mm_list, list) { if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && uectx == ctx->iu.ue_ctx) return ctx; @@ -169,7 +111,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, { struct sgsn_mm_ctx *ctx; - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + llist_for_each_entry(ctx, &sgsn->mm_list, list) { if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) && gprs_ra_id_equals(raid, &ctx->ra)) return ctx; @@ -193,7 +135,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli, if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL) return NULL; - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + llist_for_each_entry(ctx, &sgsn->mm_list, list) { if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli || gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) && gprs_ra_id_equals(raid, &ctx->ra)) @@ -207,7 +149,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi) { struct sgsn_mm_ctx *ctx; - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + llist_for_each_entry(ctx, &sgsn->mm_list, list) { if (p_tmsi == ctx->p_tmsi || (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi)) return ctx; @@ -219,7 +161,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) { struct sgsn_mm_ctx *ctx; - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + llist_for_each_entry(ctx, &sgsn->mm_list, list) { if (!strcmp(imsi, ctx->imsi)) return ctx; } @@ -261,7 +203,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t rate_ctr_id) INIT_LLIST_HEAD(&ctx->pdp_list); - llist_add(&ctx->list, &sgsn_mm_ctxts); + llist_add(&ctx->list, &sgsn->mm_list); return ctx; @@ -293,11 +235,8 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli, memcpy(&ctx->ra, raid, sizeof(ctx->ra)); ctx->ran_type = MM_CTX_T_GERAN_Gb; ctx->gb.tlli = tlli; - ctx->ciph_algo = sgsn->cfg.cipher; osmo_fsm_inst_update_id_f(ctx->gb.mm_state_fsm, "%" PRIu32, tlli); - LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n", - get_value_string(gprs_cipher_names, ctx->ciph_algo)); return ctx; } @@ -435,283 +374,6 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, return NULL; } -/* you don't want to use this directly, call sgsn_create_pdp_ctx() */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, - struct sgsn_ggsn_ctx *ggsn, - uint8_t nsapi) -{ - struct sgsn_pdp_ctx *pdp; - - pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); - if (pdp) - return NULL; - - pdp = talloc_zero(tall_sgsn_ctx, struct sgsn_pdp_ctx); - if (!pdp) - return NULL; - - pdp->mm = mm; - pdp->ggsn = ggsn; - pdp->nsapi = nsapi; - pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); - if (!pdp->ctrg) { - LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n"); - talloc_free(pdp); - return NULL; - } - llist_add(&pdp->list, &mm->pdp_list); - sgsn_ggsn_ctx_add_pdp(pdp->ggsn, pdp); - llist_add(&pdp->g_list, &sgsn_pdp_ctxts); - - return pdp; -} - -/* - * This function will not trigger any GSM DEACT PDP ACK messages, so you - * probably want to call sgsn_delete_pdp_ctx() instead if the connection - * isn't detached already. - */ -void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp) -{ - struct sgsn_signal_data sig_data; - - OSMO_ASSERT(pdp->mm != NULL); - - /* There might still be pending callbacks in libgtp. So the parts of - * this object relevant to GTP need to remain intact in this case. */ - - LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n"); - - if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Force the deactivation of the SNDCP layer */ - if (pdp->mm->gb.llme) - sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi); - } - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pdp; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data); - - /* Detach from MM context */ - pdp_ctx_detach_mm_ctx(pdp); - if (pdp->ggsn) - sgsn_delete_pdp_ctx(pdp); -} - -/* - * Don't call this function directly unless you know what you are doing. - * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or - * implementation dependent abnormal ones sgsn_pdp_ctx_terminate. - */ -void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) -{ - struct sgsn_signal_data sig_data; - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pdp; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data); - - rate_ctr_group_free(pdp->ctrg); - if (pdp->mm) - llist_del(&pdp->list); - if (pdp->ggsn) - sgsn_ggsn_ctx_remove_pdp(pdp->ggsn, pdp); - llist_del(&pdp->g_list); - - /* _if_ we still have a library handle, at least set it to NULL - * to avoid any dereferences of the now-deleted PDP context from - * sgsn_libgtp:cb_data_ind() */ - if (pdp->lib) { - struct pdp_t *lib = pdp->lib; - LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still " - "has a libgtp handle attached to it, this shouldn't " - "happen!\n"); - osmo_generate_backtrace(); - lib->priv = NULL; - } - - talloc_free(pdp); -} - -void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc) -{ - bool pending = osmo_timer_pending(&ggc->echo_timer); - - /* Only enable if allowed by policy and at least 1 pdp ctx exists against ggsn */ - if (!llist_empty(&ggc->pdp_list) && ggc->echo_interval) { - if (!pending) - osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); - } else { - if (pending) - osmo_timer_del(&ggc->echo_timer); - } -} - -/* GGSN contexts */ -static void echo_timer_cb(void *data) -{ - struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data; - sgsn_ggsn_echo_req(ggc); - osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); -} - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - ggc = talloc_zero(tall_sgsn_ctx, struct sgsn_ggsn_ctx); - if (!ggc) - return NULL; - - ggc->id = id; - ggc->gtp_version = 1; - ggc->remote_restart_ctr = -1; - /* if we are called from config file parse, this gsn doesn't exist yet */ - ggc->gsn = sgsn->gsn; - INIT_LLIST_HEAD(&ggc->pdp_list); - osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc); - llist_add(&ggc->list, &sgsn_ggsn_ctxts); - - return ggc; -} - -void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) -{ - OSMO_ASSERT(llist_empty(&ggc->pdp_list)); - llist_del(&ggc->list); - talloc_free(ggc); -} - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { - if (id == ggc->id) - return ggc; - } - return NULL; -} - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr) -{ - struct sgsn_ggsn_ctx *ggc; - - llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { - if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) - return ggc; - } - return NULL; -} - - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - ggc = sgsn_ggsn_ctx_by_id(id); - if (!ggc) - ggc = sgsn_ggsn_ctx_alloc(id); - return ggc; -} - -/* APN contexts */ - -static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - actx = talloc_zero(tall_sgsn_ctx, struct apn_ctx); - if (!actx) - return NULL; - actx->name = talloc_strdup(actx, ap_name); - actx->imsi_prefix = talloc_strdup(actx, imsi_prefix); - - llist_add_tail(&actx->list, &sgsn_apn_ctxts); - - return actx; -} - -void sgsn_apn_ctx_free(struct apn_ctx *actx) -{ - llist_del(&actx->list); - talloc_free(actx); -} - -struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi) -{ - struct apn_ctx *actx; - struct apn_ctx *found_actx = NULL; - size_t imsi_prio = 0; - size_t name_prio = 0; - size_t name_req_len = strlen(name); - - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - size_t name_ref_len, imsi_ref_len; - const char *name_ref_start, *name_match_start; - - imsi_ref_len = strlen(actx->imsi_prefix); - if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0) - continue; - - if (imsi_ref_len < imsi_prio) - continue; - - /* IMSI matches */ - - name_ref_start = &actx->name[0]; - if (name_ref_start[0] == '*') { - /* Suffix match */ - name_ref_start += 1; - name_ref_len = strlen(name_ref_start); - if (name_ref_len > name_req_len) - continue; - } else { - name_ref_len = strlen(name_ref_start); - if (name_ref_len != name_req_len) - continue; - } - - name_match_start = name + (name_req_len - name_ref_len); - if (strcasecmp(name_match_start, name_ref_start) != 0) - continue; - - /* IMSI and name match */ - - if (imsi_ref_len == imsi_prio && name_ref_len < name_prio) - /* Lower priority, skip */ - continue; - - imsi_prio = imsi_ref_len; - name_prio = name_ref_len; - found_actx = actx; - } - return found_actx; -} - -struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - if (strcasecmp(name, actx->name) == 0 && - strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) - return actx; - } - return NULL; -} - -struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - actx = sgsn_apn_ctx_by_name(name, imsi_prefix); - if (!actx) - actx = sgsn_apn_ctx_alloc(name, imsi_prefix); - - return actx; -} - uint32_t sgsn_alloc_ptmsi(void) { struct sgsn_mm_ctx *mm; @@ -747,7 +409,7 @@ restart: goto restart; } - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { + llist_for_each_entry(mm, &sgsn->mm_list, list) { if (mm->p_tmsi == ptmsi) { if (!max_retries--) goto failed; @@ -762,64 +424,6 @@ failed: return GSM_RESERVED_TMSI; } -void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx) -{ - /* the MM context can be deleted while the GGSN is not reachable or - * if has been crashed. */ - if (pctx->mm && pctx->mm->gmm_fsm->state == ST_GMM_REGISTERED_NORMAL) { - gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true); - sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx); - } else { - /* FIXME: GPRS paging in case MS is SUSPENDED */ - LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN " - "recovery\n"); - /* FIXME: how to tell this to libgtp? */ - sgsn_pdp_ctx_free(pctx); - } -} - -/* High-level function to be called in case a GGSN has disappeared or - * otherwise lost state (recovery procedure). It will detach all related pdp ctx - * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can - * be kept alive to allow handling later message which contained the Recovery IE. */ -int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except) -{ - int num = 0; - - struct sgsn_pdp_ctx *pdp, *pdp2; - llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) { - if (pdp == except) - continue; - sgsn_ggsn_ctx_drop_pdp(pdp); - num++; - } - - return num; -} - -int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn) -{ - return sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, NULL); -} - -void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) -{ - llist_add(&pdp->ggsn_list, &ggc->pdp_list); - sgsn_ggsn_ctx_check_echo_timer(ggc); -} -void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) -{ - llist_del(&pdp->ggsn_list); - sgsn_ggsn_ctx_check_echo_timer(ggc); - if (pdp->destroy_ggsn) - sgsn_ggsn_ctx_free(pdp->ggsn); - pdp->ggsn = NULL; - /* Drop references to libgtp since the conn is down */ - if (pdp->lib) - pdp_freepdp(pdp->lib); - pdp->lib = NULL; -} - void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx != NULL); @@ -894,7 +498,7 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, insert_extra(tp, mmctx->subscr->sgsn_data, pdp); continue; } - if (!llist_empty(&sgsn_apn_ctxts)) { + if (!llist_empty(&sgsn->apn_list)) { apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi); /* Not configured */ if (apn_ctx == NULL) @@ -947,13 +551,13 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, if (apn_ctx != NULL) { ggsn = apn_ctx->ggsn; - } else if (llist_empty(&sgsn_apn_ctxts)) { + } else if (llist_empty(&sgsn->apn_list)) { /* No configuration -> use GGSN 0 */ - ggsn = sgsn_ggsn_ctx_by_id(0); + ggsn = sgsn_ggsn_ctx_by_id(sgsn, 0); } else if (allow_any_apn && (selected_apn_str == NULL || strlen(selected_apn_str) == 0)) { /* No APN given and no default configuration -> Use GGSN 0 */ - ggsn = sgsn_ggsn_ctx_by_id(0); + ggsn = sgsn_ggsn_ctx_by_id(sgsn, 0); } else { /* No matching configuration found */ LOGMMCTXP(LOGL_NOTICE, mmctx, @@ -978,70 +582,3 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, return ggsn; } - -static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) -{ - struct sgsn_mm_ctx *mmctx = NULL; - - llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) { - if (llme == mmctx->gb.llme) { - gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE); - return; - } - } - - /* No MM context found */ - LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n", - llme->tlli); - gprs_llgmm_unassign(llme); -} - -static void sgsn_llme_check_cb(void *data_) -{ - struct gprs_llc_llme *llme, *llme_tmp; - struct timespec now_tp; - time_t now, age; - time_t max_age = gprs_max_time_to_idle(); - - int rc; - - rc = osmo_clock_gettime(CLOCK_MONOTONIC, &now_tp); - OSMO_ASSERT(rc >= 0); - now = now_tp.tv_sec; - - LOGP(DGPRS, LOGL_DEBUG, - "Checking for inactive LLMEs, time = %u\n", (unsigned)now); - - llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) { - if (llme->age_timestamp == GPRS_LLME_RESET_AGE) - llme->age_timestamp = now; - - age = now - llme->age_timestamp; - - if (age > max_age || age < 0) { - LOGP(DGPRS, LOGL_INFO, - "Inactivity timeout for TLLI 0x%08x, age %d\n", - llme->tlli, (int)age); - sgsn_llme_cleanup_free(llme); - } - } - - osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); -} - -struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx) -{ - struct sgsn_instance *inst; - inst = talloc_zero(talloc_ctx, struct sgsn_instance); - inst->cfg.gtp_statedir = talloc_strdup(inst, "./"); - inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; - inst->cfg.require_authentication = true; /* only applies if auth_policy is REMOTE */ - inst->cfg.gsup_server_port = OSMO_GSUP_PORT; - return inst; -} - -void sgsn_inst_init(struct sgsn_instance *sgsn) -{ - osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL); - osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); -} diff --git a/src/sgsn/pdpctx.c b/src/sgsn/pdpctx.c new file mode 100644 index 000000000..e77942040 --- /dev/null +++ b/src/sgsn/pdpctx.c @@ -0,0 +1,158 @@ +/* PDP context functionality */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> + +#include <osmocom/sgsn/pdpctx.h> +#include <osmocom/sgsn/mmctx.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gprs_llc_xid.h> +#include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sm.h> +#include <osmocom/sgsn/gtp.h> + +static const struct rate_ctr_desc pdpctx_ctr_description[] = { + { "udata:packets:in", "User Data Messages ( In)" }, + { "udata:packets:out", "User Data Messages (Out)" }, + { "udata:bytes:in", "User Data Bytes ( In)" }, + { "udata:bytes:out", "User Data Bytes (Out)" }, +}; + +static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { + .group_name_prefix = "sgsn:pdpctx", + .group_description = "SGSN PDP Context Statistics", + .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), + .ctr_desc = pdpctx_ctr_description, + .class_id = OSMO_STATS_CLASS_SUBSCRIBER, +}; + +/* you don't want to use this directly, call sgsn_create_pdp_ctx() */ +struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, + struct sgsn_ggsn_ctx *ggsn, + uint8_t nsapi) +{ + struct sgsn_pdp_ctx *pdp; + + pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); + if (pdp) + return NULL; + + pdp = talloc_zero(sgsn, struct sgsn_pdp_ctx); + if (!pdp) + return NULL; + + pdp->mm = mm; + pdp->ggsn = ggsn; + pdp->nsapi = nsapi; + pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); + if (!pdp->ctrg) { + LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n"); + talloc_free(pdp); + return NULL; + } + llist_add(&pdp->list, &mm->pdp_list); + sgsn_ggsn_ctx_add_pdp(pdp->ggsn, pdp); + llist_add(&pdp->g_list, &sgsn->pdp_list); + + return pdp; +} + +/* + * This function will not trigger any GSM DEACT PDP ACK messages, so you + * probably want to call sgsn_delete_pdp_ctx() instead if the connection + * isn't detached already. + */ +void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp) +{ + struct sgsn_signal_data sig_data; + + OSMO_ASSERT(pdp->mm != NULL); + + /* There might still be pending callbacks in libgtp. So the parts of + * this object relevant to GTP need to remain intact in this case. */ + + LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n"); + + if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { + /* Force the deactivation of the SNDCP layer */ + if (pdp->mm->gb.llme) + sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi); + } + + memset(&sig_data, 0, sizeof(sig_data)); + sig_data.pdp = pdp; + osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data); + + /* Detach from MM context */ + pdp_ctx_detach_mm_ctx(pdp); + if (pdp->ggsn) + sgsn_delete_pdp_ctx(pdp); +} + +/* + * Don't call this function directly unless you know what you are doing. + * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or + * implementation dependent abnormal ones sgsn_pdp_ctx_terminate. + */ +void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) +{ + struct sgsn_signal_data sig_data; + + memset(&sig_data, 0, sizeof(sig_data)); + sig_data.pdp = pdp; + osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data); + + if (osmo_timer_pending(&pdp->timer)) { + LOGPDPCTXP(LOGL_ERROR, pdp, "Freeing PDP ctx with timer %u pending\n", pdp->T); + osmo_timer_del(&pdp->timer); + } + + rate_ctr_group_free(pdp->ctrg); + if (pdp->mm) + llist_del(&pdp->list); + if (pdp->ggsn) + sgsn_ggsn_ctx_remove_pdp(pdp->ggsn, pdp); + llist_del(&pdp->g_list); + + /* _if_ we still have a library handle, at least set it to NULL + * to avoid any dereferences of the now-deleted PDP context from + * sgsn_libgtp:cb_data_ind() */ + if (pdp->lib) { + struct pdp_t *lib = pdp->lib; + LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still " + "has a libgtp handle attached to it, this shouldn't " + "happen!\n"); + osmo_generate_backtrace(); + lib->priv = NULL; + } + + talloc_free(pdp); +} diff --git a/src/sgsn/sgsn.c b/src/sgsn/sgsn.c new file mode 100644 index 000000000..6619bf263 --- /dev/null +++ b/src/sgsn/sgsn.c @@ -0,0 +1,215 @@ +/* SGSN instance */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> +#include <osmocom/core/backtrace.h> +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/ports.h> +#include <osmocom/gprs/gprs_ns2.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gsm/apn.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsup.h> + +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_sm.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_gmm_attach.h> +#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> +#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> +#include <osmocom/sgsn/gprs_gmm_fsm.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/pdpctx.h> + +#include <pdp.h> + +#include <time.h> + +#define GPRS_LLME_CHECK_TICK 30 + +extern struct osmo_tdef sgsn_T_defs[]; + +static const struct rate_ctr_desc sgsn_ctr_description[] = { + { "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" }, + { "llc:ul_bytes", "Count successful received LLC bytes (encrypt & fcs correct)" }, + { "llc:dl_packets", "Count successful sent LLC packets before giving it to the bssgp layer" }, + { "llc:ul_packets", "Count successful received LLC packets (encrypt & fcs correct)" }, + { "gprs:attach_requested", "Received attach requests" }, + { "gprs:attach_accepted", "Sent attach accepts" }, + { "gprs:attach_rejected", "Sent attach rejects" }, + { "gprs:detach_requested", "Received detach requests" }, + { "gprs:detach_acked", "Sent detach acks" }, + { "gprs:routing_area_requested", "Received routing area requests" }, + { "gprs:routing_area_requested", "Sent routing area acks" }, + { "gprs:routing_area_requested", "Sent routing area rejects" }, + { "pdp:activate_requested", "Received activate requests" }, + { "pdp:activate_rejected", "Sent activate rejects" }, + { "pdp:activate_accepted", "Sent activate accepts" }, + { "pdp:request_activated", "unused" }, + { "pdp:request_activate_rejected", "unused" }, + { "pdp:modify_requested", "unused" }, + { "pdp:modify_accepted", "unused" }, + { "pdp:dl_deactivate_requested", "Sent deactivate requests" }, + { "pdp:dl_deactivate_accepted", "Sent deactivate accepted" }, + { "pdp:ul_deactivate_requested", "Received deactivate requests" }, + { "pdp:ul_deactivate_accepted", "Received deactivate accepts" }, +}; + +static const struct rate_ctr_group_desc sgsn_ctrg_desc = { + "sgsn", + "SGSN Overall Statistics", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(sgsn_ctr_description), + sgsn_ctr_description, +}; + +static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) +{ + struct sgsn_mm_ctx *mmctx = NULL; + + llist_for_each_entry(mmctx, &sgsn->mm_list, list) { + if (llme == mmctx->gb.llme) { + gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE); + return; + } + } + + /* No MM context found */ + LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n", + llme->tlli); + gprs_llgmm_unassign(llme); +} + +static void sgsn_llme_check_cb(void *data_) +{ + struct gprs_llc_llme *llme, *llme_tmp; + struct timespec now_tp; + time_t now, age; + time_t max_age = gprs_max_time_to_idle(); + + int rc; + + rc = osmo_clock_gettime(CLOCK_MONOTONIC, &now_tp); + OSMO_ASSERT(rc >= 0); + now = now_tp.tv_sec; + + LOGP(DGPRS, LOGL_DEBUG, + "Checking for inactive LLMEs, time = %u\n", (unsigned)now); + + llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) { + if (llme->age_timestamp == GPRS_LLME_RESET_AGE) + llme->age_timestamp = now; + + age = now - llme->age_timestamp; + + if (age > max_age || age < 0) { + LOGP(DGPRS, LOGL_INFO, + "Inactivity timeout for TLLI 0x%08x, age %d\n", + llme->tlli, (int)age); + sgsn_llme_cleanup_free(llme); + } + } + + osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); +} + +static int sgsn_instance_talloc_destructor(struct sgsn_instance *sgi) +{ + sgsn_cdr_release(sgi); + osmo_timer_del(&sgi->llme_timer); + rate_ctr_group_free(sgi->rate_ctrs); + return 0; +} + +struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx) +{ + struct sgsn_instance *inst; + inst = talloc_zero(talloc_ctx, struct sgsn_instance); + + talloc_set_destructor(inst, sgsn_instance_talloc_destructor); + + inst->cfg.gtp_statedir = talloc_strdup(inst, "./"); + inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; + inst->cfg.require_authentication = true; /* only applies if auth_policy is REMOTE */ + inst->cfg.gsup_server_port = OSMO_GSUP_PORT; + + inst->cfg.T_defs = sgsn_T_defs; + osmo_tdefs_reset(inst->cfg.T_defs); + inst->cfg.T_defs_gtp = gtp_T_defs; + osmo_tdefs_reset(inst->cfg.T_defs_gtp); + + inst->rate_ctrs = rate_ctr_group_alloc(inst, &sgsn_ctrg_desc, 0); + OSMO_ASSERT(inst->rate_ctrs); + + INIT_LLIST_HEAD(&inst->apn_list); + INIT_LLIST_HEAD(&inst->ggsn_list); + INIT_LLIST_HEAD(&inst->mme_list); + INIT_LLIST_HEAD(&inst->mm_list); + INIT_LLIST_HEAD(&inst->pdp_list); + + osmo_timer_setup(&inst->llme_timer, sgsn_llme_check_cb, NULL); + osmo_timer_schedule(&inst->llme_timer, GPRS_LLME_CHECK_TICK, 0); + /* These are mostly setting up stuff not related to VTY cfg, so they can be set up here: */ + sgsn_auth_init(inst); + sgsn_cdr_init(inst); + return inst; +} + +/* To be called after VTY config parsing: */ +int sgsn_inst_init(struct sgsn_instance *sgsn) +{ + int rc; + + /* start control interface after reading config for + * ctrl_vty_get_bind_addr() */ + sgsn->ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_SGSN, NULL); + if (!sgsn->ctrlh) { + LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n"); + return -EIO; + } + + rc = sgsn_ctrl_cmds_install(); + if (rc != 0) { + LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n"); + return -EFAULT; + } + + rc = gprs_subscr_init(sgsn); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n"); + return rc; + } + return 0; +} diff --git a/src/sgsn/sgsn_auth.c b/src/sgsn/sgsn_auth.c index b8d803590..cbff6f8ba 100644 --- a/src/sgsn/sgsn_auth.c +++ b/src/sgsn/sgsn_auth.c @@ -22,7 +22,7 @@ #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/core/utils.h> #include <osmocom/sgsn/sgsn.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_subscriber.h> #include <osmocom/sgsn/debug.h> diff --git a/src/sgsn/sgsn_cdr.c b/src/sgsn/sgsn_cdr.c index a50b4dfdd..1536c135a 100644 --- a/src/sgsn/sgsn_cdr.c +++ b/src/sgsn/sgsn_cdr.c @@ -27,6 +27,9 @@ #include <osmocom/gsm/apn.h> #include <osmocom/sgsn/vty.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/pdpctx.h> +#include <osmocom/sgsn/mmctx.h> #include <gtp.h> #include <pdp.h> @@ -38,10 +41,6 @@ #include <stdio.h> #include <inttypes.h> -/* TODO...avoid going through a global */ -extern struct sgsn_instance *sgsn; -extern struct ctrl_handle *g_ctrlh; - /** * The CDR module will generate an entry like: * @@ -64,7 +63,7 @@ extern struct ctrl_handle *g_ctrlh; static void send_cdr_trap(char *value) { - if (ctrl_cmd_send_trap(g_ctrlh, "cdr-v1", value) < 0) + if (ctrl_cmd_send_trap(sgsn->ctrlh, "cdr-v1", value) < 0) LOGP(DGPRS, LOGL_ERROR, "Failed to create and send TRAP cdr-v1\n"); } @@ -299,3 +298,8 @@ int sgsn_cdr_init(struct sgsn_instance *sgsn) return 0; } + +void sgsn_cdr_release(struct sgsn_instance *sgsn) +{ + osmo_signal_unregister_handler(SS_SGSN, handle_sgsn_sig, sgsn); +} diff --git a/src/sgsn/sgsn_ctrl.c b/src/sgsn/sgsn_ctrl.c index ad91d25a5..069304abd 100644 --- a/src/sgsn/sgsn_ctrl.c +++ b/src/sgsn/sgsn_ctrl.c @@ -21,20 +21,19 @@ #include <osmocom/ctrl/control_if.h> #include <osmocom/ctrl/control_cmd.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> +#include <osmocom/sgsn/pdpctx.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/debug.h> #include <pdp.h> -extern vector ctrl_node_vec; - static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) { struct sgsn_mm_ctx *mm; cmd->reply = talloc_strdup(cmd, ""); - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { + llist_for_each_entry(mm, &sgsn->mm_list, list) { char *addr = NULL; struct sgsn_pdp_ctx *pdp; @@ -43,7 +42,7 @@ static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) llist_for_each_entry(pdp, &mm->pdp_list, list) addr = gprs_pdpaddr2str(pdp->lib->eua.v, - pdp->lib->eua.l); + pdp->lib->eua.l, false); cmd->reply = talloc_asprintf_append( cmd->reply, diff --git a/src/sgsn/sgsn_libgtp.c b/src/sgsn/sgsn_libgtp.c index c20c8dcfe..9edd0c60e 100644 --- a/src/sgsn/sgsn_libgtp.c +++ b/src/sgsn/sgsn_libgtp.c @@ -34,7 +34,7 @@ #include <netinet/in.h> #include <arpa/inet.h> -#include "bscconfig.h" +#include "config.h" #include <osmocom/core/talloc.h> #include <osmocom/core/select.h> @@ -45,9 +45,9 @@ #include <osmocom/sgsn/signal.h> #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/sgsn.h> -#include <osmocom/sgsn/gprs_gb.h> +#include <osmocom/sgsn/gprs_ns.h> #include <osmocom/sgsn/gprs_llc.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_sm.h> #include <osmocom/sgsn/gprs_subscriber.h> @@ -55,6 +55,11 @@ #include <osmocom/sgsn/gprs_ranap.h> #include <osmocom/sgsn/gprs_gmm_fsm.h> #include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp_mme.h> +#include <osmocom/sgsn/sgsn_rim.h> +#include <osmocom/sgsn/gprs_bssgp.h> +#include <osmocom/sgsn/pdpctx.h> #include <gtp.h> #include <pdp.h> @@ -167,6 +172,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, pdp->hisaddr0 = ggsn->remote_addr; pdp->hisaddr1 = ggsn->remote_addr; //pdp->cch_pdp = 512; /* Charging Flat Rate */ + pdp->radio_pri = 0x4; /* MS provided APN, subscription was verified by the caller */ pdp->selmode = 0xFC | 0x00; @@ -225,18 +231,11 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS); } - if (qos_len <= 3) { - pdp->qos_req.l = qos_len + 1; - if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) - pdp->qos_req.l = sizeof(pdp->qos_req.v); - pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */ - memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1); - } else { - pdp->qos_req.l = qos_len; - if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) - pdp->qos_req.l = sizeof(pdp->qos_req.v); - memcpy(pdp->qos_req.v, qos, pdp->qos_req.l); - } + pdp->qos_req.l = qos_len + 1; + if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) + pdp->qos_req.l = sizeof(pdp->qos_req.v); + pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */ + memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1); /* charging characteristics if present */ if (TLVP_LEN(tp, OSMO_IE_GSM_CHARG_CHAR) >= sizeof(pdp->cch_pdp)) @@ -311,11 +310,16 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, return pctx; } -/* SGSN wants to delete a PDP context */ +/* SGSN wants to delete a PDP context, send first DeleteCtxReq on the GTP side, + then upon DeleteCtx ACK it will send DeactPdpAcc to the MS if still + connected. */ int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx) { LOGPDPCTXP(LOGL_INFO, pctx, "Delete PDP Context\n"); + OSMO_ASSERT(pctx->ggsn); + OSMO_ASSERT(pctx->lib); + /* FIXME: decide if we need teardown or not ! */ return gtp_delete_context_req2(pctx->ggsn->gsn, pctx->lib, pctx, 1); } @@ -413,7 +417,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) } /* Check for cause value if it was really successful */ - if (cause != GTPCAUSE_ACC_REQ) { + if (!gtp_cause_successful(cause)) { reject_cause = cause_map(gtp2sm_cause_map, cause, GSM_CAUSE_ACT_REJ_GGSN); goto reject; @@ -473,6 +477,70 @@ void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc) gtp_echo_req(ggc->gsn, ggc->gtp_version, ggc, &ggc->remote_addr); } +int sgsn_mme_ran_info_req(struct sgsn_mme_ctx *mme, const struct bssgp_ran_information_pdu *pdu) +{ + char ri_src_str[64], ri_dest_str[64]; + int ri_len; + struct msgb *msg; + struct bssgp_normal_hdr *bgph; + int rc; + uint8_t ri_buf[64]; + uint8_t *ri_ptr = &ri_buf[0]; + struct sockaddr_in sk_in = { + .sin_family = AF_INET, + .sin_port = htons(GTP1C_PORT), + .sin_addr = mme->remote_addr, + }; + + msg = bssgp_encode_rim_pdu(pdu); + if (!msg) { + LOGMME(mme, DRIM, LOGL_ERROR, "Tx GTP RAN Information Relay: failed to encode pdu\n"); + return -EINVAL; + } + bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg); + DEBUGP(DLBSSGP, "Tx GTP RAN Information Relay: RIM-PDU:%s, src=%s, dest=%s\n", + bssgp_pdu_str(bgph->pdu_type), + bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src), + bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &pdu->routing_info_dest)); + + if ((ri_len = bssgp_create_rim_ri(ri_ptr, &pdu->routing_info_dest)) < 0) { + ri_ptr = NULL; + ri_len = 0; + } + + rc = gtp_ran_info_relay_req(mme->sgsn->gsn, &sk_in, msgb_data(msg), msgb_length(msg), + ri_ptr, ri_len, pdu->routing_info_dest.discr); + msgb_free(msg); + return rc; +} + +/* Confirmation of a PDP Context Update */ +static int update_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) +{ + struct sgsn_pdp_ctx *pctx = cbp; + int rc; + + LOGPDPCTXP(LOGL_INFO, pctx, "Received Update PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + + /* 3GPP TS 29.060 "7.3.4": + * "If the SGSN receives an Update PDP Context Response with a Cause + * value other than "Request accepted", it shall abort the update of the + * PDP context."" + * "If the SGSN receives an Update PDP Context Response with + * a Cause value "Non-existent", it shall delete the PDP Context." + */ + if (cause != GTPCAUSE_NON_EXIST) + return 0; /* Nothing to do */ + + LOGPDPCTXP(LOGL_INFO, pctx, "PDP CTX we tried to update doesn't exist in " + "the GGSN anymore, deleting it locally.\n"); + + rc = gtp_freepdp(pctx->ggsn->gsn, pctx->lib); + /* related mmctx is torn down in cb_delete_context called by gtp_freepdp() */ + return rc; +} + /* Confirmation of a PDP Context Delete */ static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) { @@ -528,12 +596,12 @@ static int echo_conf(void *cbp, bool timeout) } /* Any message received by GGSN contains a recovery IE */ -static int cb_recovery2(struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery) +static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery) { struct sgsn_ggsn_ctx *ggsn; struct sgsn_pdp_ctx *pctx = NULL; - ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr); + ggsn = sgsn_ggsn_ctx_by_addr(sgsn, &peer->sin_addr); if (!ggsn) { LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n"); return -EINVAL; @@ -571,6 +639,8 @@ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) return echo_conf(cbp, cause == EOF); case GTP_CREATE_PDP_REQ: return create_pdp_conf(pdp, cbp, cause); + case GTP_UPDATE_PDP_REQ: + return update_pdp_conf(pdp, cbp, cause); case GTP_DELETE_PDP_REQ: return delete_pdp_conf(pdp, cbp, cause); default: @@ -613,6 +683,74 @@ static int cb_extheader_ind(struct sockaddr_in *peer) return 0; } +static int cb_gtp_ran_info_relay_ind(struct sockaddr_in *peer, union gtpie_member **ie) +{ + char addrbuf[INET_ADDRSTRLEN]; + struct sgsn_mme_ctx *mme = sgsn_mme_ctx_by_addr(sgsn, &peer->sin_addr); + if (!mme) { + LOGP(DGTP, LOGL_NOTICE, "Rx GTP RAN Information Relay from unknown MME %s\n", + inet_ntop(AF_INET, &peer->sin_addr, addrbuf, sizeof(addrbuf))); + return -ECONNREFUSED; + } + + LOGMME(mme, DGTP, LOGL_INFO, "Rx GTP RAN Information Relay\n"); + + int rc; + unsigned int len = 0; + struct msgb *msg = bssgp_msgb_alloc(); + + uint8_t rim_ra_encoded[256]; + unsigned int rim_ra_encoded_len = 0; + struct bssgp_rim_routing_info rim_ra; + + unsigned int rim_ra_discr_encoded_len = 0; + uint8_t rim_ra_discr; + + /* Read RIM Routing Address Discriminator (optional) */ + rc = gtpie_gettlv(ie, GTPIE_RIM_RA_DISCR, 0, &rim_ra_discr_encoded_len, &rim_ra_discr, + sizeof(rim_ra_discr)); + if (rc || rim_ra_discr_encoded_len <= 0) { + LOGMME(mme, DGTP, LOGL_NOTICE, "Rx GTP RAN Information Relay: No RIM Routing Address Discriminator IE found!\n"); + + /* It is not an error when the RIM ROUTING ADDRESS DISCRIMINATOR IE is missing. The RIM ROUTING ADDRESS + * DISCRIMINATOR IE is an optional IE. When it is missing, the RIM Routing Address shall be processed + * as an RNC address ("0001") See also: 3GPP TS 29.060 */ + rim_ra_discr = BSSGP_RIM_ROUTING_INFO_UTRAN; + } + + /* Read RIM Routing Address (optional) */ + rc = gtpie_gettlv(ie, GTPIE_RIM_ROUT_ADDR, 0, &rim_ra_encoded_len, rim_ra_encoded, sizeof(rim_ra_encoded)); + if (rc || rim_ra_encoded_len <= 0) { + LOGMME(mme, DGTP, LOGL_ERROR, "Rx GTP RAN Information Relay: No RIM Routing Address IE found!\n"); + + /* TODO: The (usually included) RIM ROUTING ADDRESS field is an optional field. However, we cannot + * proceed without a destination address. A possible way to fix this would be a default route that + * can be configured via the VTY. */ + goto ret_error; + } else { + rc = bssgp_parse_rim_ra(&rim_ra, rim_ra_encoded, rim_ra_encoded_len, rim_ra_discr); + if (rc < 0) { + LOGMME(mme, DGTP, LOGL_ERROR, + "Rx GTP RAN Information Relay: Failed parsing RIM Routing Address/RIM Routing Address Discriminator IE!\n"); + goto ret_error; + } + } + + if (gtpie_gettlv(ie, GTPIE_RAN_T_CONTAIN, 0, &len, msgb_data(msg), 4096) || len <= 0) { + LOGMME(mme, DGTP, LOGL_ERROR, "Rx GTP RAN Information Relay: No Transparent Container IE found!\n"); + goto ret_error; + } + msgb_put(msg, len); + msgb_bssgph(msg) = msg->data; + msgb_nsei(msg) = 0; + + return sgsn_rim_rx_from_gtp(msg, &rim_ra); + +ret_error: + msgb_free(msg); + return -EINVAL; +} + /* Called whenever we receive a DATA packet */ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) { @@ -660,16 +798,29 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) switch (mm->gmm_fsm->state) { case ST_GMM_REGISTERED_SUSPENDED: - /* initiate PS PAGING procedure */ - gprs_gb_page_ps_ra(mm); - /* FIXME: queue the packet we received from GTP */ - break; + LOGMMCTXP(LOGL_INFO, mm, "Dropping DL packet for MS in GMM state %s\n", + osmo_fsm_inst_state_name(mm->gmm_fsm)); + msgb_free(msg); + return -1; case ST_GMM_REGISTERED_NORMAL: - OSMO_ASSERT(mm->gb.mm_state_fsm->state != ST_MM_IDLE); - if (mm->gb.mm_state_fsm->state == ST_MM_STANDBY) - gprs_gb_page_ps_ra(mm); - - /* FIXME: queue the packet we received from GTP */ + switch (mm->gb.mm_state_fsm->state) { + case ST_MM_IDLE: + LOGP(DGPRS, LOGL_ERROR, "Dropping DL packet for MS in MM state %s\n", + osmo_fsm_inst_state_name(mm->gb.mm_state_fsm)); + msgb_free(msg); + return -1; + case ST_MM_READY: + /* Go ahead */ + break; + case ST_MM_STANDBY: + LOGMMCTXP(LOGL_INFO, mm, "Paging MS in GMM state %s, MM state %s\n", + osmo_fsm_inst_state_name(mm->gmm_fsm), + osmo_fsm_inst_state_name(mm->gb.mm_state_fsm)); + sgsn_bssgp_page_ps_ra(mm); + + /* FIXME: queue the packet we received from GTP */ + break; + } break; default: LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state " @@ -678,20 +829,20 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) return -1; } - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len); - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len); + rate_ctr_inc(rate_ctr_group_get_ctr(pdp->ctrg, PDP_CTR_PKTS_UDATA_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(pdp->ctrg, PDP_CTR_BYTES_UDATA_OUT), len); + rate_ctr_inc(rate_ctr_group_get_ctr(mm->ctrg, GMM_CTR_PKTS_UDATA_OUT)); + rate_ctr_add(rate_ctr_group_get_ctr(mm->ctrg, GMM_CTR_BYTES_UDATA_OUT), len); /* It is easier to have a global count */ pdp->cdr_bytes_out += len; - return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi], + return sndcp_sn_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi], pdp->nsapi, mm); } /* Called by SNDCP when it has received/re-assembled a N-PDU */ -int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, +int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) { struct sgsn_mm_ctx *mmctx; @@ -716,10 +867,10 @@ int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, return -EIO; } - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len); + rate_ctr_inc(rate_ctr_group_get_ctr(pdp->ctrg, PDP_CTR_PKTS_UDATA_IN)); + rate_ctr_add(rate_ctr_group_get_ctr(pdp->ctrg, PDP_CTR_BYTES_UDATA_IN), npdu_len); + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_UDATA_IN)); + rate_ctr_add(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_BYTES_UDATA_IN), npdu_len); /* It is easier to have a global count */ pdp->cdr_bytes_in += npdu_len; @@ -733,7 +884,7 @@ static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what) struct sgsn_instance *sgi = fd->data; int rc; - if (!(what & BSC_FD_READ)) + if (!(what & OSMO_FD_READ)) return 0; switch (fd->priv_nr) { @@ -771,31 +922,19 @@ int sgsn_gtp_init(struct sgsn_instance *sgi) if (gsn->mode != GTP_MODE_SGSN) return -EINVAL; - sgi->gtp_fd0.fd = gsn->fd0; - sgi->gtp_fd0.priv_nr = 0; - sgi->gtp_fd0.data = sgi; - sgi->gtp_fd0.when = BSC_FD_READ; - sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; + osmo_fd_setup(&sgi->gtp_fd0, gsn->fd0, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 0); rc = osmo_fd_register(&sgi->gtp_fd0); if (rc < 0) return rc; - sgi->gtp_fd1c.fd = gsn->fd1c; - sgi->gtp_fd1c.priv_nr = 1; - sgi->gtp_fd1c.data = sgi; - sgi->gtp_fd1c.when = BSC_FD_READ; - sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; + osmo_fd_setup(&sgi->gtp_fd1c, gsn->fd1c, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 1); rc = osmo_fd_register(&sgi->gtp_fd1c); if (rc < 0) { osmo_fd_unregister(&sgi->gtp_fd0); return rc; } - sgi->gtp_fd1u.fd = gsn->fd1u; - sgi->gtp_fd1u.priv_nr = 2; - sgi->gtp_fd1u.data = sgi; - sgi->gtp_fd1u.when = BSC_FD_READ; - sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; + osmo_fd_setup(&sgi->gtp_fd1u, gsn->fd1u, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 2); rc = osmo_fd_register(&sgi->gtp_fd1u); if (rc < 0) { osmo_fd_unregister(&sgi->gtp_fd0); @@ -806,10 +945,11 @@ int sgsn_gtp_init(struct sgsn_instance *sgi) /* Register callbackcs with libgtp */ gtp_set_cb_delete_context(gsn, cb_delete_context); gtp_set_cb_conf(gsn, cb_conf); - gtp_set_cb_recovery2(gsn, cb_recovery2); + gtp_set_cb_recovery3(gsn, cb_recovery3); gtp_set_cb_data_ind(gsn, cb_data_ind); gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); + gtp_set_cb_ran_info_relay_ind(gsn, cb_gtp_ran_info_relay_ind); return 0; } diff --git a/src/sgsn/sgsn_main.c b/src/sgsn/sgsn_main.c index 7d9a85116..d6afdef52 100644 --- a/src/sgsn/sgsn_main.c +++ b/src/sgsn/sgsn_main.c @@ -39,15 +39,18 @@ #include <osmocom/core/rate_ctr.h> #include <osmocom/core/logging.h> #include <osmocom/core/stats.h> +#include <osmocom/core/sockaddr_str.h> -#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_ns2.h> #include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_bssgp_bss.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> #include <osmocom/vty/stats.h> #include <osmocom/vty/ports.h> #include <osmocom/vty/misc.h> +#include <osmocom/vty/cpu_sched_vty.h> #include <osmocom/ctrl/control_vty.h> @@ -56,15 +59,18 @@ #include <osmocom/sgsn/vty.h> #include <osmocom/sgsn/sgsn.h> #include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sndcp.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_ranap.h> - -#include <osmocom/ctrl/control_if.h> -#include <osmocom/ctrl/ports.h> +#include <osmocom/sgsn/gprs_ns.h> +#include <osmocom/sgsn/gprs_bssgp.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/gtp.h> #include <gtp.h> +#include <osmocom/sgsn/sgsn_rim.h> -#include "../../bscconfig.h" +#include "../../config.h" #if BUILD_IU #include <osmocom/sigtran/osmo_ss7.h> @@ -76,9 +82,8 @@ #include <getopt.h> void *tall_sgsn_ctx; -struct ctrl_handle *g_ctrlh; -struct gprs_ns_inst *sgsn_nsi; +struct gprs_ns2_inst *sgsn_nsi; static int daemonize = 0; const char *openbsc_copyright = "Copyright (C) 2010 Harald Welte and On-Waves\r\n" @@ -92,60 +97,18 @@ const char *openbsc_copyright = struct sgsn_instance *sgsn; -/* call-back function for the NS protocol */ -static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, - struct msgb *msg, uint16_t bvci) -{ - int rc = 0; - - switch (event) { - case GPRS_NS_EVT_UNIT_DATA: - /* hand the message into the BSSGP implementation */ - rc = bssgp_rcvmsg(msg); - break; - default: - LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); - if (msg) - msgb_free(msg); - rc = -EIO; - break; - } - return rc; -} - -/* call-back function for the BSSGP protocol */ +/* call-back function for the BSSGP protocol. + * Must be left here so that we can add a new one in tests/sgsn_test */ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { - struct osmo_bssgp_prim *bp; - bp = container_of(oph, struct osmo_bssgp_prim, oph); - - switch (oph->sap) { - case SAP_BSSGP_LL: - switch (oph->primitive) { - case PRIM_BSSGP_UL_UD: - return gprs_llc_rcvmsg(oph->msg, bp->tp); - } - break; - case SAP_BSSGP_GMM: - switch (oph->primitive) { - case PRIM_BSSGP_GMM_SUSPEND: - return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); - case PRIM_BSSGP_GMM_RESUME: - return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, - bp->u.resume.suspend_ref); - } - break; - case SAP_BSSGP_NM: - break; - } - return 0; + return sgsn_bssgp_rx_prim(oph); } -static void signal_handler(int signal) +static void signal_handler(int signum) { - fprintf(stdout, "signal %u received\n", signal); + fprintf(stdout, "signal %u received\n", signum); - switch (signal) { + switch (signum) { case SIGINT: case SIGTERM: osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); @@ -153,8 +116,17 @@ static void signal_handler(int signal) exit(0); break; case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ + /* in case of abort, we want to obtain a talloc report and + * then run default SIGABRT handler, who will generate coredump + * and abort the process. abort() should do this for us after we + * return, but program wouldn't exit if an external SIGABRT is + * received. + */ + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_sgsn_ctx, stderr); + signal(SIGABRT, SIG_DFL); + raise(SIGABRT); + break; case SIGUSR1: talloc_report(tall_vty_ctx, stderr); talloc_report_full(tall_sgsn_ctx, stderr); @@ -167,43 +139,36 @@ static void signal_handler(int signal) } } -/* NSI that BSSGP uses when transmitting on NS */ -extern struct gprs_ns_inst *bssgp_nsi; - -int sgsn_vty_is_config_node(struct vty *vty, int node) +static int sgsn_vty_go_parent(struct vty *vty) { - /* So far the SGSN has no nested nodes that need parent node - * declaration, except for the ss7 vty nodes. */ - switch (node) { + switch (vty->node) { case SGSN_NODE: - return 1; + vty->node = CONFIG_NODE; + break; + case MME_NODE: + vty->node = SGSN_NODE; + vty->index = NULL; + break; default: #if BUILD_IU - return osmo_ss7_is_config_node(vty, node); + osmo_ss7_vty_go_parent(vty); #else - return 0; + vty->node = CONFIG_NODE; + vty->index = NULL; #endif + break; } -} -int sgsn_vty_go_parent(struct vty *vty) -{ - /* So far the SGSN has no nested nodes that need parent node - * declaration, except for the ss7 vty nodes. */ -#if BUILD_IU - return osmo_ss7_vty_go_parent(vty); -#else - vty->node = CONFIG_NODE; - vty->index = NULL; - return 0; -#endif + return vty->node; } +/* NSI that BSSGP uses when transmitting on NS */ +extern struct gprs_ns_inst *bssgp_nsi; + static struct vty_app_info vty_info = { .name = "OsmoSGSN", .version = PACKAGE_VERSION, .go_parent_cb = sgsn_vty_go_parent, - .is_config_node = sgsn_vty_is_config_node, }; static void print_help(void) @@ -216,12 +181,43 @@ static void print_help(void) printf(" -s --disable-color\n"); printf(" -c --config-file\tThe config file to use [%s]\n", CONFIG_FILE_DEFAULT); printf(" -e --log-level number\tSet a global log level\n"); + + printf("\nVTY reference generation:\n"); + printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"); + printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n"); } +static void handle_long_options(const char *prog_name, const int long_option) +{ + static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT; + + switch (long_option) { + case 1: + vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg); + if (vty_ref_mode < 0) { + fprintf(stderr, "%s: Unknown VTY reference generation " + "mode '%s'\n", prog_name, optarg); + exit(2); + } + break; + case 2: + fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n", + get_value_string(vty_ref_gen_mode_names, vty_ref_mode), + get_value_string(vty_ref_gen_mode_desc, vty_ref_mode)); + vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode); + exit(0); + default: + fprintf(stderr, "%s: error parsing cmdline options\n", prog_name); + exit(2); + } +} + + static void handle_options(int argc, char **argv) { while (1) { int option_index = 0, c; + static int long_option = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"debug", 1, 0, 'd'}, @@ -231,6 +227,8 @@ static void handle_options(int argc, char **argv) {"timestamp", 0, 0, 'T'}, { "version", 0, 0, 'V' }, {"log-level", 1, 0, 'e'}, + {"vty-ref-mode", 1, &long_option, 1}, + {"vty-ref-xml", 0, &long_option, 2}, {NULL, 0, 0, 0} }; @@ -240,6 +238,9 @@ static void handle_options(int argc, char **argv) break; switch (c) { + case 0: + handle_long_options(argv[0], long_option); + break; case 'h': //print_usage(); print_help(); @@ -271,6 +272,11 @@ static void handle_options(int argc, char **argv) break; } } + + if (argc > optind) { + fprintf(stderr, "Unsupported positional arguments on command line\n"); + exit(2); + } } /* default categories */ @@ -302,16 +308,6 @@ static struct log_info_cat gprs_categories[] = { .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_NOTICE, }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, [DLLC] = { .name = "DLLC", .description = "GPRS Logical Link Control Protocol (LLC)", @@ -347,6 +343,11 @@ static struct log_info_cat gprs_categories[] = { .description = "GPRS Tunnelling Protocol (GTP)", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DRIM] = { + .name = "DRIM", + .description = "RAN Information Management (RIM)", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, }; static const struct log_info gprs_log_info = { @@ -391,6 +392,7 @@ int main(int argc, char **argv) osmo_stats_vty_add_cmds(); sgsn_vty_init(&sgsn->cfg); ctrl_vty_init(tall_sgsn_ctx); + osmo_cpu_sched_vty_init(tall_sgsn_ctx); #if BUILD_IU OSMO_ASSERT(osmo_ss7_init() == 0); @@ -398,6 +400,24 @@ int main(int argc, char **argv) osmo_sccp_vty_init(); #endif + rate_ctr_init(tall_sgsn_ctx); + + logging_vty_add_deprecated_subsys(tall_sgsn_ctx, "bssgp"); + logging_vty_add_deprecated_subsys(tall_sgsn_ctx, "ns"); + + sgsn_nsi = gprs_ns2_instantiate(tall_sgsn_ctx, &gprs_ns_prim_cb, NULL); + if (!sgsn_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + sgsn->cfg.nsi = sgsn_nsi; + bssgp_set_bssgp_callback(sgsn_bssgp_dispatch_ns_unitdata_req_cb, sgsn_nsi); + + gprs_ns2_vty_init(sgsn_nsi); + bssgp_vty_init(); + gprs_llc_vty_init(); + gprs_sndcp_vty_init(); + handle_options(argc, argv); /* Backwards compatibility: for years, the default config file name was @@ -415,30 +435,6 @@ int main(int argc, char **argv) osmo_talloc_replace_string(sgsn, &sgsn->config_file, CONFIG_FILE_DEFAULT); } - rate_ctr_init(tall_sgsn_ctx); - - gprs_ns_set_log_ss(DNS); - bssgp_set_log_ss(DBSSGP); - - sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_sgsn_ctx); - if (!sgsn_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - bssgp_nsi = sgsn->cfg.nsi = sgsn_nsi; - - gprs_llc_init("/usr/local/lib/osmocom/crypt/"); - sgsn_rate_ctr_init(); - sgsn_inst_init(sgsn); - - gprs_ns_vty_init(bssgp_nsi); - bssgp_vty_init(); - gprs_llc_vty_init(); - gprs_sndcp_vty_init(); - sgsn_auth_init(sgsn); - sgsn_cdr_init(sgsn); - /* FIXME: register signal handler for SS_L_NS */ - rc = sgsn_parse_config(sgsn->config_file); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Error in config file\n"); @@ -446,25 +442,11 @@ int main(int argc, char **argv) } /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_sgsn_ctx, NULL, - vty_get_bind_addr(), OSMO_VTY_PORT_SGSN); + rc = telnet_init_default(tall_sgsn_ctx, NULL, OSMO_VTY_PORT_SGSN); if (rc < 0) exit(1); - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_SGSN, NULL); - if (!g_ctrlh) { - LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n"); - exit(1); - } - - if (sgsn_ctrl_cmds_install() != 0) { - LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n"); - exit(1); - } - + gprs_llc_init(sgsn->cfg.crypt_cipher_plugin_path); rc = sgsn_gtp_init(sgsn); if (rc) { @@ -473,22 +455,9 @@ int main(int argc, char **argv) } else LOGP(DGPRS, LOGL_NOTICE, "libGTP v%s initialized\n", gtp_version()); - rc = gprs_subscr_init(sgsn); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n"); - exit(2); - } - - rc = gprs_ns_nsip_listen(sgsn_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); - exit(2); - } - - rc = gprs_ns_frgre_listen(sgsn_nsi); + rc = sgsn_inst_init(sgsn); if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " - "socket. Do you have CAP_NET_RAW?\n"); + LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n"); exit(2); } @@ -507,8 +476,8 @@ int main(int argc, char **argv) "OsmoSGSN", (23 << 3) + 4, OSMO_SS7_ASP_PROT_M3UA, - 0, NULL, - 0, "127.0.0.1"); + 0, "localhost", + 0, "localhost"); if (!sccp) { printf("Setting up SCCP client failed.\n"); return 8; diff --git a/src/sgsn/sgsn_rim.c b/src/sgsn/sgsn_rim.c new file mode 100644 index 000000000..40e1f9033 --- /dev/null +++ b/src/sgsn/sgsn_rim.c @@ -0,0 +1,127 @@ + +#include <stdio.h> + +#include <errno.h> +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/vty/logging.h> +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_bssgp_rim.h> +#include <osmocom/sgsn/sgsn_rim.h> +#include <osmocom/sgsn/gtp_mme.h> +#include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/sgsn.h> + +static int sgsn_bssgp_fwd_rim_to_geran(const struct bssgp_ran_information_pdu *pdu) +{ + struct bssgp_bvc_ctx *bvc_ctx; + OSMO_ASSERT(pdu->routing_info_dest.discr == BSSGP_RIM_ROUTING_INFO_GERAN); + + /* Resolve RIM ROUTING ADDRESS to a BVC context */ + bvc_ctx = btsctx_by_raid_cid(&pdu->routing_info_dest.geran.raid, pdu->routing_info_dest.geran.cid); + if (!bvc_ctx) { + LOGP(DRIM, LOGL_ERROR, "Unable to find NSEI for destination cell %s\n", + bssgp_rim_ri_name(&pdu->routing_info_dest)); + return -EINVAL; + } + + /* Forward PDU to the NSEI of the resolved BVC context */ + return bssgp_tx_rim(pdu, bvc_ctx->nsei); +} + +static int sgsn_bssgp_fwd_rim_to_geran_encoded(struct msgb *msg, struct bssgp_rim_routing_info *rim_routing_address) +{ + struct bssgp_bvc_ctx *bvc_ctx; + OSMO_ASSERT(rim_routing_address->discr == BSSGP_RIM_ROUTING_INFO_GERAN); + + /* Resolve RIM ROUTING ADDRESS to a BVC context */ + bvc_ctx = btsctx_by_raid_cid(&rim_routing_address->geran.raid, rim_routing_address->geran.cid); + if (!bvc_ctx) { + LOGP(DRIM, LOGL_ERROR, "Unable to find NSEI for destination cell %s\n", + bssgp_rim_ri_name(rim_routing_address)); + return -EINVAL; + } + + /* Forward PDU to the NSEI of the resolved BVC context */ + return bssgp_tx_rim_encoded(msg, bvc_ctx->nsei); +} + +static int sgsn_bssgp_fwd_rim_to_eutran(const struct bssgp_ran_information_pdu *pdu) +{ + struct sgsn_mme_ctx *mme; + OSMO_ASSERT(pdu->routing_info_dest.discr == BSSGP_RIM_ROUTING_INFO_EUTRAN); + + mme = sgsn_mme_ctx_by_route(sgsn, &pdu->routing_info_dest.eutran.tai); + if (!mme) { /* See if we have a default route configured */ + mme = sgsn_mme_ctx_by_default_route(sgsn); + if (!mme) { + LOGP(DRIM, LOGL_ERROR, "Unable to find MME for destination cell %s\n", + bssgp_rim_ri_name(&pdu->routing_info_dest)); + return -EINVAL; + } + } + + return sgsn_mme_ran_info_req(mme, pdu); +} + +/* Receive a RIM PDU from BSSGP (GERAN) */ +int sgsn_rim_rx_from_gb(struct osmo_bssgp_prim *bp, struct msgb *msg) +{ + uint16_t nsei = msgb_nsei(msg); + struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu; + + if (pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_GERAN) { + LOGP(DRIM, LOGL_ERROR, + "Rx BSSGP RIM (NSEI=%u): Expected src %s, got %s\n", nsei, + bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_GERAN), + bssgp_rim_routing_info_discr_str(pdu->routing_info_src.discr)); + goto err; + } + + switch (pdu->routing_info_dest.discr) { + case BSSGP_RIM_ROUTING_INFO_GERAN: + return sgsn_bssgp_fwd_rim_to_geran(pdu); + case BSSGP_RIM_ROUTING_INFO_EUTRAN: + return sgsn_bssgp_fwd_rim_to_eutran(pdu); + default: + /* At the moment we can only handle GERAN/EUTRAN addresses, any + * other type of address will be considered as an invalid + * address. see also: 3GPP TS 48.018, section 8c.3.1.3 + */ + LOGP(DRIM, LOGL_ERROR, + "Rx BSSGP RIM (NSEI=%u): Unsupported dst %s\n", nsei, + bssgp_rim_routing_info_discr_str(pdu->routing_info_dest.discr)); + } + + LOGP(DRIM, LOGL_INFO, "Rx BSSGP RIM (NSEI=%u): for dest cell %s\n", nsei, + bssgp_rim_ri_name(&pdu->routing_info_dest)); + +err: + /* In case of an invalid destination address we respond with + * a BSSGP STATUS PDU, see also: 3GPP TS 48.018, section 8c.3.1.3 */ + bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg); + return -1; +} + +/* Receive a RIM PDU from GTPv1C (EUTRAN) */ +int sgsn_rim_rx_from_gtp(struct msgb *msg, struct bssgp_rim_routing_info *rim_routing_address) +{ + /* TODO: In this code path, we currently only support RIM message forwarding to GERAN (BSSGP). However, it + * technically also be possible to route a message back to GTP (BSSGP_RIM_ROUTING_INFO_EUTRAN) or to + * IuPS (BSSGP_RIM_ROUTING_INFO_UTRAN) */ + if (rim_routing_address->discr != BSSGP_RIM_ROUTING_INFO_GERAN) { + LOGP(DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: Expected dst %s, got %s\n", + bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_GERAN), + bssgp_rim_routing_info_discr_str(rim_routing_address->discr)); + return -EINVAL; + } + + LOGP(DRIM, LOGL_INFO, "Rx GTP RAN Information Relay for dest cell %s\n", + bssgp_rim_ri_name(rim_routing_address)); + + return sgsn_bssgp_fwd_rim_to_geran_encoded(msg, rim_routing_address); +} diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c index 14248d1d8..29f9cd604 100644 --- a/src/sgsn/sgsn_vty.c +++ b/src/sgsn/sgsn_vty.c @@ -34,10 +34,15 @@ #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/sgsn.h> -#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_ns2.h> +#include <osmocom/sgsn/gprs_ns.h> #include <osmocom/sgsn/gprs_gmm.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_bssgp.h> +#include <osmocom/sgsn/mmctx.h> +#include <osmocom/sgsn/gtp_ggsn.h> +#include <osmocom/sgsn/gtp_mme.h> #include <osmocom/sgsn/vty.h> +#include <osmocom/sgsn/pdpctx.h> #include <osmocom/gsupclient/gsup_client.h> #include <osmocom/vty/tdef_vty.h> @@ -45,6 +50,7 @@ #include <osmocom/vty/vty.h> #include <osmocom/vty/misc.h> #include <osmocom/crypt/gprs_cipher.h> +#include <osmocom/crypt/utran_cipher.h> #include <osmocom/abis/ipa.h> #include <osmocom/gprs/gprs_bssgp.h> @@ -52,14 +58,12 @@ #include <pdp.h> #include <gtp.h> -#include "../../bscconfig.h" +#include "../../config.h" #ifdef BUILD_IU #include <osmocom/ranap/iu_client.h> #endif -extern void *tall_sgsn_ctx; - static struct sgsn_config *g_cfg = NULL; const struct value_string sgsn_auth_pol_strs[] = { @@ -94,7 +98,7 @@ const struct value_string sgsn_auth_pol_strs[] = { #define NONSPEC_X1001_SECS 5 /* wait for a RANAP Release Complete */ -static struct osmo_tdef sgsn_T_defs[] = { +struct osmo_tdef sgsn_T_defs[] = { { .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update timer (s)" }, { .T=3313, .default_val=GSM0408_T3313_SECS, .desc="Waiting for paging response timer (s)" }, { .T=3314, .default_val=GSM0408_T3314_SECS, .desc="READY timer. Force to STANDBY on expiry timer (s)" }, @@ -110,7 +114,6 @@ static struct osmo_tdef sgsn_T_defs[] = { /* non spec timers */ { .T=-1001, .default_val=NONSPEC_X1001_SECS, .desc="RANAP Release timeout. Wait for RANAP Release Complete." "On expiry release Iu connection (s)" }, - { .T=-3314, .default_val=GSM0408_T3314_SECS, .desc="Iu User inactivity timer. On expiry release Iu connection (s)" }, {} }; @@ -134,7 +137,27 @@ DEFUN(cfg_sgsn_timer, cfg_sgsn_timer_cmd, return osmo_tdef_vty_set_cmd(vty, g_cfg->T_defs, argv); } -char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) +DEFUN(show_timer_gtp, show_timer_gtp_cmd, + "show timer gtp " OSMO_TDEF_VTY_ARG_T_OPTIONAL, + SHOW_STR "Show timers\n" "GTP (libgtp) timers\n" + OSMO_TDEF_VTY_DOC_T) +{ + const char *T_arg = argc > 0 ? argv[0] : NULL; + return osmo_tdef_vty_show_cmd(vty, g_cfg->T_defs_gtp, T_arg, NULL); +} + +DEFUN(cfg_sgsn_timer_gtp, cfg_sgsn_timer_gtp_cmd, + "timer gtp " OSMO_TDEF_VTY_ARG_SET_OPTIONAL, + "Configure or show timers\n" "GTP (libgtp) timers\n" + OSMO_TDEF_VTY_DOC_SET) +{ + /* If any arguments are missing, redirect to 'show' */ + if (argc < 2) + return show_timer(self, vty, argc, argv); + return osmo_tdef_vty_set_cmd(vty, g_cfg->T_defs_gtp, argv); +} + +char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len, bool return_ipv6) { static char str[INET6_ADDRSTRLEN + 10]; @@ -147,15 +170,28 @@ char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) case PDP_TYPE_N_IETF_IPv4: if (len < 2 + 4) break; - strcpy(str, "IPv4 "); + osmo_strlcpy(str, "IPv4 ", sizeof(str)); inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5); return str; case PDP_TYPE_N_IETF_IPv6: if (len < 2 + 8) break; - strcpy(str, "IPv6 "); + osmo_strlcpy(str, "IPv6 ", sizeof(str)); inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5); return str; + case PDP_TYPE_N_IETF_IPv4v6: + if (len < 2 + 20) + break; + if (return_ipv6) { + /* The IPv6 token, (rightmost four fields) is a duplicate of + * the site prefix + subnetID (leftmost fields) in pdpa here */ + osmo_strlcpy(str, "IPv6 ", sizeof(str)); + inet_ntop(AF_INET6, pdpa+6, str+5, sizeof(str)-5); + return str; + } + osmo_strlcpy(str, "IPv4 ", sizeof(str)); + inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5); + return str; default: break; } @@ -177,19 +213,45 @@ static struct cmd_node sgsn_node = { 1, }; +static struct cmd_node mme_node = { + MME_NODE, + "%s(config-sgsn-mme)# ", + 1, +}; + +static void config_write_mme(struct vty *vty, const struct sgsn_mme_ctx *mme, const char *prefix) +{ + struct mme_rim_route *rt; + + vty_out(vty, "%smme %s%s", prefix, mme->name, VTY_NEWLINE); + + vty_out(vty, "%s gtp remote-ip %s%s", prefix, inet_ntoa(mme->remote_addr), VTY_NEWLINE); + if (mme->default_route) + vty_out(vty, "%s gtp ran-info-relay default%s", prefix, VTY_NEWLINE); + llist_for_each_entry(rt, &mme->routes, list) { + vty_out(vty, "%s gtp ran-info-relay %s %s %u%s", prefix, + osmo_mcc_name(rt->tai.mcc), osmo_mnc_name(rt->tai.mnc, rt->tai.mnc_3_digits), + rt->tai.tac, VTY_NEWLINE); + } +} + static int config_write_sgsn(struct vty *vty) { struct sgsn_ggsn_ctx *gctx; struct imsi_acl_entry *acl; struct apn_ctx *actx; struct ares_addr_node *server; + struct sgsn_mme_ctx *mme; + int i; vty_out(vty, "sgsn%s", VTY_NEWLINE); + vty_out(vty, " gtp state-dir %s%s", + g_cfg->gtp_statedir, VTY_NEWLINE); vty_out(vty, " gtp local-ip %s%s", inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE); - llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) { + llist_for_each_entry(gctx, &sgsn->ggsn_list, list) { if (gctx->id == UINT32_MAX) continue; @@ -211,10 +273,26 @@ static int config_write_sgsn(struct vty *vty) for (server = sgsn->ares_servers; server; server = server->next) vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); - if (g_cfg->cipher != GPRS_ALGO_GEA0) - vty_out(vty, " encryption %s%s", - get_value_string(gprs_cipher_names, g_cfg->cipher), - VTY_NEWLINE); + if (g_cfg->gea_encryption_mask != 0) { + vty_out(vty, " encryption gea"); + + for (i = 0; i < _GPRS_ALGO_NUM; i++) + if (g_cfg->gea_encryption_mask >> i & 1) + vty_out(vty, " %u", i); + + vty_out(vty, "%s", VTY_NEWLINE); + } + if (g_cfg->uea_encryption_mask != 0) { + vty_out(vty, " encryption uea"); + + for (i = 0; i < _OSMO_UTRAN_UEA_NUM; i++) + if (g_cfg->uea_encryption_mask >> i & 1) + vty_out(vty, " %u", i); + + vty_out(vty, "%s", VTY_NEWLINE); + } + if (g_cfg->crypt_cipher_plugin_path) + vty_out(vty, " encryption cipher-plugin-path %s%s", g_cfg->crypt_cipher_plugin_path, VTY_NEWLINE); if (g_cfg->sgsn_ipa_name) vty_out(vty, " gsup ipa-name %s%s", g_cfg->sgsn_ipa_name, VTY_NEWLINE); if (g_cfg->gsup_server_addr.sin_addr.s_addr) @@ -243,9 +321,9 @@ static int config_write_sgsn(struct vty *vty) llist_for_each_entry(acl, &g_cfg->imsi_acl, list) vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE); - if (llist_empty(&sgsn_apn_ctxts)) + if (llist_empty(&sgsn->apn_list)) vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE); - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { + llist_for_each_entry(actx, &sgsn->apn_list, list) { if (strlen(actx->imsi_prefix) > 0) vty_out(vty, " apn %s imsi-prefix %s ggsn %u%s", actx->name, actx->imsi_prefix, actx->ggsn->id, @@ -266,6 +344,7 @@ static int config_write_sgsn(struct vty *vty) vty_out(vty, " cdr interval %d%s", g_cfg->cdr.interval, VTY_NEWLINE); osmo_tdef_vty_write(vty, g_cfg->T_defs, " timer "); + osmo_tdef_vty_write(vty, g_cfg->T_defs_gtp, " timer gtp "); if (g_cfg->pcomp_rfc1144.active) { vty_out(vty, " compression rfc1144 active slots %d%s", @@ -295,6 +374,10 @@ static int config_write_sgsn(struct vty *vty) } else vty_out(vty, " no compression v42bis%s", VTY_NEWLINE); + llist_for_each_entry(mme, &sgsn->mme_list, list) { + config_write_mme(vty, mme, " "); + } + #ifdef BUILD_IU vty_out(vty, " cs7-instance-iu %u%s", g_cfg->iu.cs7_instance, VTY_NEWLINE); @@ -315,6 +398,17 @@ DEFUN(cfg_sgsn, cfg_sgsn_cmd, return CMD_SUCCESS; } +DEFUN(cfg_sgsn_state_dir, cfg_sgsn_state_dir_cmd, + "gtp state-dir PATH", + "GTP Parameters\n" + "Set the directory for the GTP State file\n" + "Local Directory\n") +{ + osmo_talloc_replace_string(sgsn, &sgsn->cfg.gtp_statedir, argv[0]); + + return CMD_SUCCESS; +} + DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd, "gtp local-ip A.B.C.D", "GTP Parameters\n" @@ -338,7 +432,7 @@ DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd, "IPv4 Address\n") { uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(sgsn, id); inet_aton(argv[1], &ggc->remote_addr); @@ -363,7 +457,7 @@ DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, "Version 0\n" "Version 1\n") { uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(sgsn, id); if (atoi(argv[1])) ggc->gtp_version = 1; @@ -381,7 +475,7 @@ DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd, "Interval between echo requests in seconds.\n") { uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(sgsn, id); ggc->echo_interval = atoi(argv[1]); @@ -400,7 +494,7 @@ DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd, NO_STR "Send an echo request to this static GGSN every interval.\n") { uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(sgsn, id); ggc->echo_interval = 0; sgsn_ggsn_ctx_check_echo_timer(ggc); @@ -441,7 +535,7 @@ static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str, struct apn_ctx *actx; struct sgsn_ggsn_ctx *ggsn; - ggsn = sgsn_ggsn_ctx_by_id(ggsn_id); + ggsn = sgsn_ggsn_ctx_by_id(sgsn, ggsn_id); if (ggsn == NULL) { vty_out(vty, "%% a GGSN with id %d has not been defined%s", ggsn_id, VTY_NEWLINE); @@ -505,8 +599,13 @@ static void vty_dump_pdp(struct vty *vty, const char *pfx, osmo_apn_to_str(apnbuf, pdp->lib->apn_use.v, pdp->lib->apn_use.l), VTY_NEWLINE); vty_out(vty, "%s PDP Address: %s%s", pfx, - gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), + gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l, false), VTY_NEWLINE); + if (pdp->lib->eua.v[1] == PDP_TYPE_N_IETF_IPv4v6) { + vty_out(vty, "%s PDP Address: %s%s", pfx, + gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l, true), + VTY_NEWLINE); + } vty_out(vty, "%s GTPv%d Local Control(%s / TEIC: 0x%08x) ", pfx, pdp->lib->version, sgsn_gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own); vty_out(vty, "Data(%s / TEID: 0x%08x)%s", @@ -614,7 +713,7 @@ DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd, argv[0], VTY_NEWLINE); return CMD_WARNING; } - vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); + vty_dump_mmctx(vty, "", mm, (argc > 1) ? 1 : 0); return CMD_SUCCESS; } @@ -623,9 +722,8 @@ DEFUN(swow_mmctx_all, show_mmctx_all_cmd, SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR) { struct sgsn_mm_ctx *mm; - - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) - vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0); + llist_for_each_entry(mm, &sgsn->mm_list, list) + vty_dump_mmctx(vty, "", mm, (argc > 0) ? 1 : 0); return CMD_SUCCESS; } @@ -636,7 +734,7 @@ DEFUN(show_pdpctx_all, show_pdpctx_all_cmd, { struct sgsn_pdp_ctx *pdp; - llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list) + llist_for_each_entry(pdp, &sgsn->pdp_list, g_list) vty_dump_pdp(vty, "", pdp); return CMD_SUCCESS; @@ -682,15 +780,19 @@ DEFUN(imsi_acl, cfg_imsi_acl_cmd, return CMD_SUCCESS; } -DEFUN(cfg_encrypt, cfg_encrypt_cmd, +DEFUN_DEPRECATED(cfg_encrypt, cfg_encrypt_cmd, "encryption (GEA0|GEA1|GEA2|GEA3|GEA4)", "Set encryption algorithm for SGSN\n" "Use GEA0 (no encryption)\n" "Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n") { enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]); + + if (strcmp(argv[0], "gea") == 0) + return CMD_SUCCESS; + if (c != GPRS_ALGO_GEA0) { - if (!gprs_cipher_supported(c)) { + if (gprs_cipher_supported(c) <= 0) { vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } @@ -702,11 +804,73 @@ DEFUN(cfg_encrypt, cfg_encrypt_cmd, } } - g_cfg->cipher = c; + g_cfg->gea_encryption_mask |= (1 << c); + + return CMD_SUCCESS; +} + +#define ENCRYPTION_STR "Set encryption algorithms for SGSN\n" + +DEFUN(cfg_encrypt2, cfg_encrypt2_cmd, + "encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]", + ENCRYPTION_STR + "GPRS Encryption Algorithm\n" + "GEAn Algorithm Number\n" + "GEAn Algorithm Number\n" + "GEAn Algorithm Number\n" + "GEAn Algorithm Number\n" + "GEAn Algorithm Number\n") +{ + int i = 0; + + g_cfg->gea_encryption_mask = 0; + for (i = 0; i < argc; i++) + g_cfg->gea_encryption_mask |= (1 << atoi(argv[i])); + + for (i = 0; i < _GPRS_ALGO_NUM; i++) { + if (g_cfg->gea_encryption_mask >> i & 1) { + + if (i == GPRS_ALGO_GEA0) + continue; + + if (gprs_cipher_supported(i) <= 0) { + vty_out(vty, "%% cipher %d is unsupported in current version%s", i, VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + if (!g_cfg->require_authentication) { + vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s", + argv[i], VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + } + } return CMD_SUCCESS; } +DEFUN(cfg_encrypt_cipher_plugin_path, cfg_encrypt_cipher_plugin_path_cmd, + "encryption cipher-plugin-path PATH", + ENCRYPTION_STR + "Path to gprs encryption cipher plugin directory\n" + "Plugin path\n") +{ + osmo_talloc_replace_string(sgsn, &sgsn->cfg.crypt_cipher_plugin_path, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_encrypt_cipher_plugin_path, cfg_no_encrypt_cipher_plugin_path_cmd, + "no encryption cipher-plugin-path PATH", + NO_STR ENCRYPTION_STR + "Path to gprs encryption cipher plugin directory\n" + "Plugin path\n") +{ + TALLOC_FREE(sgsn->cfg.crypt_cipher_plugin_path); + return CMD_SUCCESS; +} + DEFUN(cfg_authentication, cfg_authentication_cmd, "authentication (optional|required)", "Whether to enforce MS authentication in GERAN (only with auth-policy remote)\n" @@ -728,6 +892,23 @@ DEFUN(cfg_authentication, cfg_authentication_cmd, return CMD_SUCCESS; } +DEFUN(cfg_encryption_uea, cfg_encryption_uea_cmd, + "encryption uea <0-2> [<0-2>] [<0-2>]", + ENCRYPTION_STR + "UTRAN (3G) encryption algorithms to allow: 0 = UEA0 (no encryption), 1 = UEA1, 2 = UEA2.\n" + "UEAn Algorithm Number\n" + "UEAn Algorithm Number\n" + "UEAn Algorithm Number\n") +{ + unsigned int i; + + g_cfg->uea_encryption_mask = 0; + for (i = 0; i < argc; i++) + g_cfg->uea_encryption_mask |= (1 << atoi(argv[i])); + + return CMD_SUCCESS; +} + DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, "auth-policy (accept-all|closed|acl-only|remote)", "Configure the Authorization policy of the SGSN. This setting determines which subscribers are" @@ -809,8 +990,17 @@ static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int } llist_for_each_entry(pdp, &gsub->sgsn_data->pdp_list, list) { - vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'", - pdp->context_id, pdp->pdp_type, pdp->apn_str); + char ip_str[INET6_ADDRSTRLEN] = { 0 }; + + vty_out(vty, " PDP info: Id: %d, Addr(Org: 0x%02x Type: 0x%02x", + pdp->context_id, pdp->pdp_type_org, pdp->pdp_type_nr); + + if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC) + vty_out(vty, " Addr[0]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[0].u.sa, ip_str)); + if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC) + vty_out(vty, " Addr[1]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[1].u.sa, ip_str)); + + vty_out(vty, ") APN: '%s'", pdp->apn_str); if (pdp->qos_subscribed_len) vty_out(vty, " QoS: %s", osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len)); @@ -857,7 +1047,7 @@ DEFUN_HIDDEN(reset_sgsn_state, struct gprs_subscr *subscr, *tmp_subscr; struct sgsn_mm_ctx *mm, *tmp_mm; - llist_for_each_entry_safe(mm, tmp_mm, &sgsn_mm_ctxts, list) + llist_for_each_entry_safe(mm, tmp_mm, &sgsn->mm_list, list) { gsm0408_gprs_access_cancelled(mm, SGSN_ERROR_CAUSE_NONE); } @@ -1007,6 +1197,10 @@ DEFUN(update_subscr_create, update_subscr_create_cmd, } subscr = gprs_subscr_get_or_create(imsi); + if (!subscr) { + vty_out(vty, "Can not create subscriber. Out of memory.%s", imsi); + return CMD_WARNING; + } subscr->keep_in_ram = 1; gprs_subscr_put(subscr); @@ -1117,6 +1311,25 @@ DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd, return CMD_SUCCESS; } +DEFUN(page_subscr, page_subscr_info_cmd, + "page imsi IMSI", + "Send a PS paging request to subscriber\n" + "Use the IMSI to select the subscriber\n" + "The IMSI\n") +{ + const char *imsi = argv[0]; + struct sgsn_mm_ctx *mm; + + mm = sgsn_mm_ctx_by_imsi(imsi); + if (!mm) { + vty_out(vty, "No MM context for IMSI %s%s", imsi, VTY_NEWLINE); + return CMD_WARNING; + } + + sgsn_bssgp_page_ps_ra(mm); + return CMD_SUCCESS; +} + DEFUN(cfg_gsup_ipa_name, cfg_gsup_ipa_name_cmd, "gsup ipa-name NAME", @@ -1407,13 +1620,165 @@ DEFUN(cfg_sgsn_cs7_instance_iu, } #endif +DEFUN(cfg_sgsn_mme, cfg_sgsn_mme_cmd, + "mme NAME", + "Configure an MME peer\n" + "Name identifying the MME peer\n") +{ + struct sgsn_mme_ctx *mme; + + mme = sgsn_mme_ctx_find_alloc(sgsn, argv[0]); + if (!mme) + return CMD_WARNING; + + vty->node = MME_NODE; + vty->index = mme; + + return CMD_SUCCESS; +} + +DEFUN(cfg_sgsn_no_mme, cfg_sgsn_no_mme_cmd, + "no mme NAME", + NO_STR "Delete an MME peer configuration\n" + "Name identifying the MME peer\n") +{ + struct sgsn_mme_ctx *mme; + + mme = sgsn_mme_ctx_by_name(sgsn, argv[0]); + if (!mme) { + vty_out(vty, "%% MME %s doesn't exist.%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + sgsn_mme_ctx_free(mme); + + return CMD_SUCCESS; +} + +#define GTP_STR "Configure GTP connection\n" + +DEFUN(cfg_mme_remote_ip, cfg_mme_remote_ip_cmd, + "gtp remote-ip A.B.C.D", + GTP_STR "Set Remote GTP IP address\n" IP_STR) +{ + struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index; + + inet_aton(argv[0], &mme->remote_addr); + + return CMD_SUCCESS; +} + +#define RAN_INFO_STR "Configure RAN Information Relay routing\n" +#define TAI_DOC "MCC\n" "MNC\n" "TAC\n" + +DEFUN(cfg_mme_ran_info_relay_tai, cfg_mme_ran_info_relay_tai_cmd, + "gtp ran-info-relay <0-999> <0-999> <0-65535>", + GTP_STR RAN_INFO_STR TAI_DOC) +{ + struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index; + struct sgsn_mme_ctx *mme_tmp; + struct osmo_eutran_tai tai; + + const char *mcc = argv[0]; + const char *mnc = argv[1]; + const char *tac = argv[2]; + + if (osmo_mcc_from_str(mcc, &tai.mcc)) { + vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(mnc, &tai.mnc, &tai.mnc_3_digits)) { + vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE); + return CMD_WARNING; + } + tai.tac = atoi(tac); + + if ((mme_tmp = sgsn_mme_ctx_by_route(sgsn, &tai))) { + if (mme_tmp != mme) { + vty_out(vty, "%% Another MME %s already contains this route%s", + mme_tmp->name, VTY_NEWLINE); + return CMD_WARNING; + } + /* else: NO-OP, return */ + return CMD_SUCCESS; + } + + sgsn_mme_ctx_route_add(mme, &tai); + return CMD_SUCCESS; +} + +DEFUN(cfg_mme_no_ran_info_relay_tai, cfg_mme_no_ran_info_relay_tai_cmd, + "no gtp ran-info-relay <0-999> <0-999> <0-65535>", + NO_STR GTP_STR RAN_INFO_STR TAI_DOC) +{ + struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index; + struct sgsn_mme_ctx *mme_tmp; + struct osmo_eutran_tai tai; + + const char *mcc = argv[0]; + const char *mnc = argv[1]; + const char *tac = argv[2]; + + if (osmo_mcc_from_str(mcc, &tai.mcc)) { + vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(mnc, &tai.mnc, &tai.mnc_3_digits)) { + vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE); + return CMD_WARNING; + } + tai.tac = atoi(tac); + + if ((mme_tmp = sgsn_mme_ctx_by_route(sgsn, &tai))) { + if (mme_tmp != mme) { + vty_out(vty, "%% Another MME %s contains this route%s", + mme_tmp->name, VTY_NEWLINE); + return CMD_WARNING; + } + sgsn_mme_ctx_route_del(mme, &tai); + return CMD_SUCCESS; + } else { + vty_out(vty, "%% This route doesn't exist in current MME %s%s", + mme->name, VTY_NEWLINE); + return CMD_WARNING; + } +} + +DEFUN(cfg_mme_ran_info_relay_default, cfg_mme_ran_info_relay_default_cmd, + "gtp ran-info-relay default", + GTP_STR RAN_INFO_STR "Set as default route") +{ + struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index; + struct sgsn_mme_ctx *default_mme; + + if (mme->default_route) + return CMD_SUCCESS; /* NO-OP */ + + if ((default_mme = sgsn_mme_ctx_by_default_route(sgsn))) { + vty_out(vty, "%% Another MME %s is already set as default route, " + "remove it before setting it here.%s", + default_mme->name, VTY_NEWLINE); + return CMD_WARNING; + } + + mme->default_route = true; + return CMD_SUCCESS; +} + +DEFUN(cfg_mme_no_ran_info_relay_default, cfg_mme_no_ran_info_relay_default_cmd, + "no gtp ran-info-relay default", + NO_STR GTP_STR RAN_INFO_STR "Set as default route") +{ + struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index; + mme->default_route = false; + return CMD_SUCCESS; +} + int sgsn_vty_init(struct sgsn_config *cfg) { g_cfg = cfg; - g_cfg->T_defs = sgsn_T_defs; - osmo_tdefs_reset(g_cfg->T_defs); - install_element_ve(&show_sgsn_cmd); //install_element_ve(&show_mmctx_tlli_cmd); install_element_ve(&show_mmctx_imsi_cmd); @@ -1421,6 +1786,7 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element_ve(&show_pdpctx_all_cmd); install_element_ve(&show_subscr_cache_cmd); install_element_ve(&show_timer_cmd); + install_element_ve(&show_timer_gtp_cmd); install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd); install_element(ENABLE_NODE, &update_subscr_create_cmd); @@ -1428,10 +1794,12 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(ENABLE_NODE, &update_subscr_cancel_cmd); install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd); install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd); + install_element(ENABLE_NODE, &page_subscr_info_cmd); install_element(ENABLE_NODE, &reset_sgsn_state_cmd); install_element(CONFIG_NODE, &cfg_sgsn_cmd); install_node(&sgsn_node, config_write_sgsn); + install_element(SGSN_NODE, &cfg_sgsn_state_dir_cmd); install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd); install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd); //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd); @@ -1441,7 +1809,14 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(SGSN_NODE, &cfg_imsi_acl_cmd); install_element(SGSN_NODE, &cfg_auth_policy_cmd); install_element(SGSN_NODE, &cfg_authentication_cmd); + + /* order matters here: ensure we attempt to parse our new command first! */ + install_element(SGSN_NODE, &cfg_encrypt2_cmd); install_element(SGSN_NODE, &cfg_encrypt_cmd); + install_element(SGSN_NODE, &cfg_encryption_uea_cmd); + install_element(SGSN_NODE, &cfg_encrypt_cipher_plugin_path_cmd); + install_element(SGSN_NODE, &cfg_no_encrypt_cipher_plugin_path_cmd); + install_element(SGSN_NODE, &cfg_gsup_ipa_name_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); @@ -1461,6 +1836,7 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(SGSN_NODE, &cfg_grx_ggsn_cmd); install_element(SGSN_NODE, &cfg_sgsn_timer_cmd); + install_element(SGSN_NODE, &cfg_sgsn_timer_gtp_cmd); install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd); install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd); @@ -1469,6 +1845,15 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(SGSN_NODE, &cfg_comp_v42bis_cmd); install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd); + install_element(SGSN_NODE, &cfg_sgsn_mme_cmd); + install_element(SGSN_NODE, &cfg_sgsn_no_mme_cmd); + install_node(&mme_node, NULL); + install_element(MME_NODE, &cfg_mme_remote_ip_cmd); + install_element(MME_NODE, &cfg_mme_ran_info_relay_default_cmd); + install_element(MME_NODE, &cfg_mme_no_ran_info_relay_default_cmd); + install_element(MME_NODE, &cfg_mme_ran_info_relay_tai_cmd); + install_element(MME_NODE, &cfg_mme_no_ran_info_relay_tai_cmd); + #ifdef BUILD_IU install_element(SGSN_NODE, &cfg_sgsn_cs7_instance_iu_cmd); ranap_iu_vty_init(SGSN_NODE, &g_cfg->iu.rab_assign_addr_enc); @@ -1483,6 +1868,9 @@ int sgsn_parse_config(const char *config_file) /* make sure sgsn_vty_init() was called before this */ OSMO_ASSERT(g_cfg); + g_cfg->gea_encryption_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */ + g_cfg->uea_encryption_mask = (1 << OSMO_UTRAN_UEA0); /* support UEA0 by default unless specific encryption config exists */ + rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); diff --git a/src/sgsn/v42bis.c b/src/sgsn/v42bis.c index 0759cdf1c..ddca5f8c9 100644 --- a/src/sgsn/v42bis.c +++ b/src/sgsn/v42bis.c @@ -17,10 +17,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. |