aboutsummaryrefslogtreecommitdiffstats
path: root/src/sgsn
diff options
context:
space:
mode:
Diffstat (limited to 'src/sgsn')
-rw-r--r--src/sgsn/Makefile.am9
-rw-r--r--src/sgsn/apn.c123
-rw-r--r--src/sgsn/gprs_bssgp.c95
-rw-r--r--src/sgsn/gprs_gmm.c91
-rw-r--r--src/sgsn/gprs_gmm_attach.c12
-rw-r--r--src/sgsn/gprs_gmm_fsm.c8
-rw-r--r--src/sgsn/gprs_llc.c38
-rw-r--r--src/sgsn/gprs_llc_vty.c17
-rw-r--r--src/sgsn/gprs_mm_state_gb_fsm.c3
-rw-r--r--src/sgsn/gprs_mm_state_iu_fsm.c5
-rw-r--r--src/sgsn/gprs_ns.c (renamed from src/sgsn/gprs_gb.c)87
-rw-r--r--src/sgsn/gprs_ranap.c11
-rw-r--r--src/sgsn/gprs_sm.c12
-rw-r--r--src/sgsn/gprs_sndcp.c218
-rw-r--r--src/sgsn/gprs_subscriber.c7
-rw-r--r--src/sgsn/gtp_ggsn.c178
-rw-r--r--src/sgsn/gtp_mme.c6
-rw-r--r--src/sgsn/mmctx.c (renamed from src/sgsn/gprs_sgsn.c)507
-rw-r--r--src/sgsn/pdpctx.c158
-rw-r--r--src/sgsn/sgsn.c220
-rw-r--r--src/sgsn/sgsn_auth.c2
-rw-r--r--src/sgsn/sgsn_cdr.c14
-rw-r--r--src/sgsn/sgsn_ctrl.c9
-rw-r--r--src/sgsn/sgsn_libgtp.c105
-rw-r--r--src/sgsn/sgsn_main.c72
-rw-r--r--src/sgsn/sgsn_rim.c60
-rw-r--r--src/sgsn/sgsn_vty.c180
-rw-r--r--src/sgsn/v42bis.c4
28 files changed, 1344 insertions, 907 deletions
diff --git a/src/sgsn/Makefile.am b/src/sgsn/Makefile.am
index 9e4a34264..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,12 +54,16 @@ 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 \
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_gmm.c b/src/sgsn/gprs_gmm.c
index 1f68558ef..653c42527 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 },
@@ -109,9 +111,14 @@ static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T)
static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T)
{
- if (mm->T != T)
+ if (!osmo_timer_pending(&mm->timer)) {
+ LOGMMCTXP(LOGL_NOTICE, mm, "Stopping *inactive* MM timer %u\n", T);
+ return;
+ }
+ if (mm->T != T) {
LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but "
"%u is running\n", T, mm->T);
+ }
osmo_timer_del(&mm->timer);
}
@@ -916,7 +923,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;
}
@@ -1319,11 +1332,11 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
ctx->ue_cipher_mask = gprs_ms_net_cap_gea_mask(ctx->ms_network_capa.buf, msnc_len);
- if (!(ctx->ue_cipher_mask & sgsn->cfg.cipher_support_mask)) {
+ 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 "
"%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.cipher_support_mask);
+ mi_log_string, ctx->ue_cipher_mask, sgsn->cfg.gea_encryption_mask);
goto rejected;
}
@@ -1335,7 +1348,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
* 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.cipher_support_mask);
+ 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 */
@@ -1383,11 +1396,11 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx)
/* only in case SGSN offered new P-TMSI */
LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM ATTACH COMPLETE\n");
- #ifdef BUILD_IU
+#ifdef BUILD_IU
if (mmctx->iu.ue_ctx) {
ranap_iu_tx_release(mmctx->iu.ue_ctx, NULL);
}
- #endif
+#endif
mmctx_timer_stop(mmctx, 3350);
mmctx->t3350_mode = GMM_T3350_MODE_NONE;
@@ -1759,6 +1772,10 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *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 */
@@ -2306,3 +2323,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 629cc53fe..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
}
diff --git a/src/sgsn/gprs_gmm_fsm.c b/src/sgsn/gprs_gmm_fsm.c
index 78946b5b1..57e1ec3bb 100644
--- a/src/sgsn/gprs_gmm_fsm.c
+++ b/src/sgsn/gprs_gmm_fsm.c
@@ -1,6 +1,6 @@
/* GMM mobility management states on the network side, 3GPP TS 24.008 § 4.1.3.3 */
/*
- * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
@@ -80,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; */
@@ -141,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),
diff --git a/src/sgsn/gprs_llc.c b/src/sgsn/gprs_llc.c
index eea1cecfa..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>
@@ -50,10 +50,21 @@ const struct value_string gprs_llc_llme_state_names[] = {
{ 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);
@@ -212,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
@@ -288,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;
@@ -590,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);
@@ -680,7 +692,7 @@ int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command,
}
/* 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 */
@@ -691,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");
@@ -704,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];
@@ -964,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");
@@ -1018,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 */
@@ -1036,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;
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 6e812d31d..dde7b770a 100644
--- a/src/sgsn/gprs_mm_state_gb_fsm.c
+++ b/src/sgsn/gprs_mm_state_gb_fsm.c
@@ -1,6 +1,6 @@
/* TS 23.060 § 6.1.1 Mobility Management States (A/Gb mode) */
/*
- * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
@@ -27,6 +27,7 @@
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>
+#include <osmocom/sgsn/mmctx.h>
#define X(s) (1 << (s))
diff --git a/src/sgsn/gprs_mm_state_iu_fsm.c b/src/sgsn/gprs_mm_state_iu_fsm.c
index b1604f83b..c2e9c4498 100644
--- a/src/sgsn/gprs_mm_state_iu_fsm.c
+++ b/src/sgsn/gprs_mm_state_iu_fsm.c
@@ -1,6 +1,6 @@
/* TS 23.060 § 6.1.2 Mobility Management States (Iu mode) */
/*
- * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
@@ -29,6 +29,9 @@
#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))
diff --git a/src/sgsn/gprs_gb.c b/src/sgsn/gprs_ns.c
index 96157a01e..eb447facd 100644
--- a/src/sgsn/gprs_gb.c
+++ b/src/sgsn/gprs_ns.c
@@ -29,95 +29,10 @@
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/sgsn/gprs_llc.h>
-#include "bscconfig.h"
+#include "config.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, 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;
-}
-
-
-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(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
-
- return rc;
-}
-
-/* called by the bssgp layer to send NS PDUs */
-int gprs_gb_send_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);
-}
-
void gprs_ns_prim_status_cb(struct osmo_gprs_ns2_prim *nsp)
{
switch (nsp->u.status.cause) {
diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c
index ead20f776..5e0d8edc6 100644
--- a/src/sgsn/gprs_ranap.c
+++ b/src/sgsn/gprs_ranap.c
@@ -21,7 +21,7 @@
*
*/
-#include "bscconfig.h"
+#include "config.h"
#include <gtp.h>
#include <osmocom/core/rate_ctr.h>
@@ -37,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)
@@ -145,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);
diff --git a/src/sgsn/gprs_sm.c b/src/sgsn/gprs_sm.c
index 6c09c4f97..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
@@ -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;
diff --git a/src/sgsn/gprs_sndcp.c b/src/sgsn/gprs_sndcp.c
index 6692f1ae5..3eae127fc 100644
--- a/src/sgsn/gprs_sndcp.c
+++ b/src/sgsn/gprs_sndcp.c
@@ -32,7 +32,7 @@
#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>
@@ -41,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 */
@@ -66,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;
@@ -84,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];
@@ -173,7 +176,7 @@ struct sndcp_common_hdr {
uint8_t first:1;
uint8_t spare:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* 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));
@@ -185,7 +188,7 @@ struct sndcp_comp_hdr {
uint8_t pcomp:4;
uint8_t dcomp:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t dcomp:4, pcomp:4;
#endif
} __attribute__((packed));
@@ -198,7 +201,7 @@ struct sndcp_udata_hdr {
/* octet 4 */
uint8_t npdu_low;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* 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
@@ -221,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;
@@ -260,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;
@@ -275,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;
@@ -289,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;
@@ -336,53 +389,20 @@ 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;
+ expnd = decompress_segment(sne, msg, npdu, npdu_len, &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, "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);
+ret_free:
/* we must free the memory we allocated above; ownership is not transferred
* downwards in the call above */
msgb_free(msg);
@@ -522,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;
@@ -544,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;
@@ -651,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;
@@ -709,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;
@@ -760,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;
@@ -830,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;
- }
-
- /* 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, (unsigned int *)&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, "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)
@@ -1042,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...)
@@ -1092,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() */
@@ -1134,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 943fbc3dc..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);
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
index 4fe804d94..3d7ec76b6 100644
--- a/src/sgsn/gtp_mme.c
+++ b/src/sgsn/gtp_mme.c
@@ -1,6 +1,6 @@
/* TS 29.060 § 7.5.14 RAN Information Management Messages */
/*
- * (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
@@ -26,8 +26,6 @@
#include <osmocom/sgsn/gtp_mme.h>
#include <osmocom/sgsn/sgsn.h>
-extern void *tall_sgsn_ctx;
-
static bool _eutran_tai_equal(const struct osmo_eutran_tai *t1, const struct osmo_eutran_tai *t2)
{
return t1->mcc == t2->mcc &&
@@ -39,7 +37,7 @@ static bool _eutran_tai_equal(const struct osmo_eutran_tai *t1, const struct osm
struct sgsn_mme_ctx *sgsn_mme_ctx_alloc(struct sgsn_instance *sgsn, const char *name)
{
struct sgsn_mme_ctx *mme;
- mme = talloc_zero(tall_sgsn_ctx, struct sgsn_mme_ctx);
+ mme = talloc_zero(sgsn, struct sgsn_mme_ctx);
if (!mme)
return NULL;
diff --git a/src/sgsn/gprs_sgsn.c b/src/sgsn/mmctx.c
index 304ecc516..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,6 +27,8 @@
#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>
@@ -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;
@@ -432,288 +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);
-
- 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);
-}
-
-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;
@@ -749,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;
@@ -764,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);
@@ -896,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)
@@ -949,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,
@@ -980,72 +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;
-
- INIT_LLIST_HEAD(&inst->mme_list);
- 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..57a2c38a9
--- /dev/null
+++ b/src/sgsn/sgsn.c
@@ -0,0 +1,220 @@
+/* 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/crypt/gprs_cipher.h>
+#include <osmocom/crypt/utran_cipher.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.gea_encryption_mask = (1 << GPRS_ALGO_GEA0); /* no encryption */
+ inst->cfg.uea_encryption_mask = (1 << OSMO_UTRAN_UEA2) | (1 << OSMO_UTRAN_UEA1);
+ 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 1ac3b44cd..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,8 +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>
@@ -228,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))
@@ -421,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;
@@ -600,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;
@@ -699,9 +695,46 @@ static int cb_gtp_ran_info_relay_ind(struct sockaddr_in *peer, union gtpie_membe
LOGMME(mme, DGTP, LOGL_INFO, "Rx GTP RAN Information Relay\n");
+ int rc;
unsigned int len = 0;
- struct msgb *msg = msgb_alloc(4096, "gtpcv1_ran_info");
- struct bssgp_ran_information_pdu pdu;
+ 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");
@@ -710,13 +743,8 @@ static int cb_gtp_ran_info_relay_ind(struct sockaddr_in *peer, union gtpie_membe
msgb_put(msg, len);
msgb_bssgph(msg) = msg->data;
msgb_nsei(msg) = 0;
- if (bssgp_parse_rim_pdu(&pdu, msg) < 0) {
- LOGMME(mme, DGTP, LOGL_ERROR, "Rx GTP RAN Information Relay: Failed parsing Transparent Container IE!\n");
- goto ret_error;
- }
- msgb_free(msg);
- return sgsn_rim_rx_from_gtp(&pdu, mme);
+ return sgsn_rim_rx_from_gtp(msg, &rim_ra);
ret_error:
msgb_free(msg);
@@ -775,15 +803,24 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
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) {
+ 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));
- gprs_gb_page_ps_ra(mm);
- }
+ sgsn_bssgp_page_ps_ra(mm);
- /* FIXME: queue the packet we received from GTP */
+ /* FIXME: queue the packet we received from GTP */
+ break;
+ }
break;
default:
LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state "
@@ -800,12 +837,12 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int 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;
@@ -908,7 +945,7 @@ 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);
diff --git a/src/sgsn/sgsn_main.c b/src/sgsn/sgsn_main.c
index af9757a89..d6afdef52 100644
--- a/src/sgsn/sgsn_main.c
+++ b/src/sgsn/sgsn_main.c
@@ -59,17 +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/sgsn/gprs_gb.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>
@@ -81,7 +82,6 @@
#include <getopt.h>
void *tall_sgsn_ctx;
-struct ctrl_handle *g_ctrlh;
struct gprs_ns2_inst *sgsn_nsi;
static int daemonize = 0;
@@ -97,34 +97,11 @@ const char *openbsc_copyright =
struct sgsn_instance *sgsn;
-/* 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;
- case SAP_BSSGP_RIM:
- return sgsn_rim_rx_from_gb(bp, oph->msg);
- }
- return 0;
+ return sgsn_bssgp_rx_prim(oph);
}
static void signal_handler(int signum)
@@ -434,19 +411,12 @@ int main(int argc, char **argv)
exit(1);
}
sgsn->cfg.nsi = sgsn_nsi;
- bssgp_set_bssgp_callback(gprs_gb_send_cb, sgsn_nsi);
-
- gprs_llc_init("/usr/local/lib/osmocom/crypt/");
- sgsn_rate_ctr_init();
- sgsn_inst_init(sgsn);
-
+ 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();
- sgsn_auth_init(sgsn);
- sgsn_cdr_init(sgsn);
handle_options(argc, argv);
@@ -472,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) {
@@ -499,9 +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);
+ rc = sgsn_inst_init(sgsn);
if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n");
+ LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n");
exit(2);
}
diff --git a/src/sgsn/sgsn_rim.c b/src/sgsn/sgsn_rim.c
index f28ba60bf..40e1f9033 100644
--- a/src/sgsn/sgsn_rim.c
+++ b/src/sgsn/sgsn_rim.c
@@ -12,6 +12,7 @@
#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>
@@ -20,6 +21,7 @@ static int sgsn_bssgp_fwd_rim_to_geran(const struct bssgp_ran_information_pdu *p
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",
@@ -27,10 +29,27 @@ static int sgsn_bssgp_fwd_rim_to_geran(const struct bssgp_ran_information_pdu *p
return -EINVAL;
}
- /* Forward PDU as it is to the correct interface */
+ /* 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;
@@ -88,36 +107,21 @@ err:
return -1;
}
-/* Receive a RIM PDU from GTPvC1 (EUTRAN) */
-int sgsn_rim_rx_from_gtp(struct bssgp_ran_information_pdu *pdu, struct sgsn_mme_ctx *mme)
+/* Receive a RIM PDU from GTPv1C (EUTRAN) */
+int sgsn_rim_rx_from_gtp(struct msgb *msg, struct bssgp_rim_routing_info *rim_routing_address)
{
- struct sgsn_mme_ctx *mme_tmp;
- if (pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_EUTRAN) {
- LOGMME(mme, DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: Expected src %s, got %s\n",
- bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_EUTRAN),
- bssgp_rim_routing_info_discr_str(pdu->routing_info_src.discr));
- return -EINVAL;
- }
-
- if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
- LOGMME(mme, 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(pdu->routing_info_dest.discr));
- return -EINVAL;
- }
-
- mme_tmp = sgsn_mme_ctx_by_route(sgsn, &pdu->routing_info_src.eutran.tai);
- if (!mme_tmp)/* See if we have a default route configured */
- mme_tmp = sgsn_mme_ctx_by_default_route(sgsn);
- if (mme != mme_tmp) {
- LOGP(DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: "
- "Source MME doesn't have RIM routing configured for TAI: %s\n",
- bssgp_rim_ri_name(&pdu->routing_info_src));
+ /* 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;
}
- LOGMME(mme, DRIM, LOGL_INFO, "Rx GTP RAN Information Relay for dest cell %s\n",
- bssgp_rim_ri_name(&pdu->routing_info_dest));
+ 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(pdu);
+ 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 e0e26775e..6ca5a7d25 100644
--- a/src/sgsn/sgsn_vty.c
+++ b/src/sgsn/sgsn_vty.c
@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#include <time.h>
#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
@@ -35,11 +37,14 @@
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/gprs/gprs_ns2.h>
-#include <osmocom/sgsn/gprs_gb.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>
@@ -47,6 +52,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>
@@ -54,14 +60,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[] = {
@@ -96,7 +100,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)" },
@@ -135,7 +139,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];
@@ -148,15 +172,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;
}
@@ -216,7 +253,7 @@ static int config_write_sgsn(struct vty *vty)
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;
@@ -238,15 +275,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_support_mask != 0) {
+ if (g_cfg->gea_encryption_mask != 0) {
vty_out(vty, " encryption gea");
for (i = 0; i < _GPRS_ALGO_NUM; i++)
- if (g_cfg->cipher_support_mask >> i & 1)
+ 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)
@@ -275,9 +323,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,
@@ -298,6 +346,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",
@@ -357,6 +406,11 @@ DEFUN(cfg_sgsn_state_dir, cfg_sgsn_state_dir_cmd,
"Set the directory for the GTP State file\n"
"Local Directory\n")
{
+ if (mkdir(argv[0], 0755) == -1 && errno != EEXIST) {
+ vty_out(vty, "%% Failed to create state-dir: %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
osmo_talloc_replace_string(sgsn, &sgsn->cfg.gtp_statedir, argv[0]);
return CMD_SUCCESS;
@@ -385,7 +439,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);
@@ -410,7 +464,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;
@@ -428,7 +482,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]);
@@ -447,7 +501,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);
@@ -488,7 +542,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);
@@ -552,8 +606,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",
@@ -670,7 +729,7 @@ 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)
+ llist_for_each_entry(mm, &sgsn->mm_list, list)
vty_dump_mmctx(vty, "", mm, (argc > 0) ? 1 : 0);
return CMD_SUCCESS;
@@ -682,7 +741,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;
@@ -752,14 +811,16 @@ DEFUN_DEPRECATED(cfg_encrypt, cfg_encrypt_cmd,
}
}
- g_cfg->cipher_support_mask |= (1 << 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>]",
- "Set encryption algorithms for SGSN\n"
+ ENCRYPTION_STR
"GPRS Encryption Algorithm\n"
"GEAn Algorithm Number\n"
"GEAn Algorithm Number\n"
@@ -769,12 +830,12 @@ DEFUN(cfg_encrypt2, cfg_encrypt2_cmd,
{
int i = 0;
- g_cfg->cipher_support_mask = 0;
+ g_cfg->gea_encryption_mask = 0;
for (i = 0; i < argc; i++)
- g_cfg->cipher_support_mask |= (1 << atoi(argv[i]));
+ g_cfg->gea_encryption_mask |= (1 << atoi(argv[i]));
for (i = 0; i < _GPRS_ALGO_NUM; i++) {
- if (g_cfg->cipher_support_mask >> i & 1) {
+ if (g_cfg->gea_encryption_mask >> i & 1) {
if (i == GPRS_ALGO_GEA0)
continue;
@@ -796,6 +857,27 @@ DEFUN(cfg_encrypt2, cfg_encrypt2_cmd,
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"
@@ -817,6 +899,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"
@@ -898,8 +997,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));
@@ -946,7 +1054,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);
}
@@ -1225,7 +1333,7 @@ DEFUN(page_subscr, page_subscr_info_cmd,
return CMD_WARNING;
}
- gprs_gb_page_ps_ra(mm);
+ sgsn_bssgp_page_ps_ra(mm);
return CMD_SUCCESS;
}
@@ -1678,9 +1786,6 @@ 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);
@@ -1688,6 +1793,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);
@@ -1714,6 +1820,9 @@ int sgsn_vty_init(struct sgsn_config *cfg)
/* 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);
@@ -1734,6 +1843,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);
@@ -1765,8 +1875,6 @@ int sgsn_parse_config(const char *config_file)
/* make sure sgsn_vty_init() was called before this */
OSMO_ASSERT(g_cfg);
- g_cfg->cipher_support_mask = 0x1; /* support GEA0 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.