diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index 14043cea5..b515abdb8 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -690,6 +690,75 @@ static void extract_subscr_hlr(struct sgsn_mm_ctx *ctx) strncpy(&ctx->hlr[0], called.number, sizeof(ctx->hlr) - 1); } +#ifdef BUILD_IU +/* Chapter 9.4.21: Service accept */ +static int gsm48_tx_gmm_service_ack(struct sgsn_mm_ctx *mm) +{ + struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE ACK"); + struct gsm48_hdr *gh; + + LOGMMCTXP(LOGL_INFO, mm, "<- GPRS SERVICE ACCEPT (P-TMSI=0x%08x)\n", mm->p_tmsi); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_SERVICE_ACK; + + /* Optional: PDP context status */ + /* Optional: MBMS context status */ + + return gsm48_gmm_sendmsg(msg, 0, mm, false); +} +#endif + +/* Chapter 9.4.22: Service reject */ +static int _tx_gmm_service_rej(struct msgb *msg, uint8_t gmm_cause, + const struct sgsn_mm_ctx *mm) +{ + struct gsm48_hdr *gh; + + LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS SERVICE REJECT: %s\n", + get_value_string(gsm48_gmm_cause_names, gmm_cause)); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_SERVICE_REJ; + gh->data[0] = gmm_cause; + + return gsm48_gmm_sendmsg(msg, 0, NULL, true); +} +static int gsm48_tx_gmm_service_rej_oldmsg(const struct msgb *old_msg, + uint8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ OLD"); + gmm_copy_id(msg, old_msg); + return _tx_gmm_service_rej(msg, gmm_cause, NULL); +} +#if 0 +-- currently unused -- +static int gsm48_tx_gmm_service_rej(struct sgsn_mm_ctx *mm, + uint8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ"); + mmctx2msgid(msg, mm); + return _tx_gmm_service_rej(msg, gmm_cause, mm); +} +#endif + +static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm); + +#ifdef BUILD_IU +void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) +{ + /* Send RAB activation requests for all PDP contexts */ + struct sgsn_pdp_ctx *pdp; + llist_for_each_entry(pdp, &ctx->pdp_list, list) { + iu_rab_act_ps(pdp->nsapi, pdp, 1); + } +} +#endif + /* Check if we can already authorize a subscriber */ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) { @@ -778,6 +847,23 @@ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) #endif return gsm48_tx_gmm_att_ack(ctx); +#ifdef BUILD_IU + case GSM48_MT_GMM_SERVICE_REQ: + /* TODO: PMM State transition */ + ctx->pending_req = 0; + rc = gsm48_tx_gmm_service_ack(ctx); + + if (ctx->iu.service.type == 1) { + activate_pdp_rabs(ctx); + } + + return rc; +#endif + case GSM48_MT_GMM_RA_UPD_REQ: + ctx->pending_req = 0; + /* Send RA UPDATE ACCEPT */ + return gsm48_tx_gmm_ra_upd_ack(ctx); + default: LOGMMCTXP(LOGL_ERROR, ctx, "only Attach Request is supported yet, " @@ -1473,6 +1559,116 @@ rejected: return rc; } +/* 3GPP TS 24.008 Section 9.4.20 Service request. + * In Iu, a UE in PMM-IDLE mode can use GSM48_MT_GMM_SERVICE_REQ to switch back + * to PMM-CONNECTED mode. */ +static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t *cur = gh->data, *mi; + uint8_t ciph_seq_nr, service_type, mi_len, mi_type; + uint32_t tmsi; + struct tlv_parsed tp; + char mi_string[GSM48_MI_SIZE]; + enum gsm48_gmm_cause reject_cause; + int rc; + + LOGMMCTXP(LOGL_INFO, ctx, "-> GMM SERVICE REQUEST "); + + /* This message is only valid in Iu mode */ + if (!msg->dst) { + LOGPC(DMM, LOGL_INFO, "Invalid if not in Iu mode\n"); + return -1; + } + + /* Skip Ciphering key sequence number 10.5.1.2 */ + ciph_seq_nr = *cur & 0x07; + + /* Service type 10.5.5.20 */ + service_type = (*cur++ >> 4) & 0x07; + + /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ + mi_len = *cur++; + mi = cur; + if (mi_len > 8) + goto err_inval; + mi_type = *mi & GSM_MI_TYPE_MASK; + cur += mi_len; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, + get_value_string(gprs_service_t_strs, service_type)); + + LOGPC(DMM, LOGL_INFO, "\n"); + + /* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */ + tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + /* Try to find MM context based on IMSI */ + if (!ctx) + ctx = sgsn_mm_ctx_by_imsi(mi_string); + if (!ctx) { + /* FIXME: We need to have a context for service request? */ + reject_cause = GMM_CAUSE_NET_FAIL; + goto rejected; + } + msgid2mmctx(ctx, msg); + break; + case GSM_MI_TYPE_TMSI: + memcpy(&tmsi, mi+1, 4); + tmsi = ntohl(tmsi); + /* Try to find MM context based on P-TMSI */ + if (!ctx) + ctx = sgsn_mm_ctx_by_ptmsi(tmsi); + if (!ctx) { + /* FIXME: We need to have a context for service request? */ + reject_cause = GMM_CAUSE_NET_FAIL; + goto rejected; + } + msgid2mmctx(ctx, msg); + break; + default: + LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with " + "MI type %s\n", gsm48_mi_type_name(mi_type)); + reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; + goto rejected; + } + + ctx->mm_state = GMM_COMMON_PROC_INIT; + + ctx->iu.service.type = service_type; + + /* TODO: Handle those only in case of accept? */ + /* Look at PDP Context Status IE and see if MS's view of + * activated/deactivated NSAPIs agrees with our view */ + if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { + const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); + process_ms_ctx_status(ctx, pdp_status); + } + + + ctx->pending_req = GSM48_MT_GMM_SERVICE_REQ; + return gsm48_gmm_authorize(ctx); + +err_inval: + LOGPC(DMM, LOGL_INFO, "\n"); + reject_cause = GMM_CAUSE_SEM_INCORR_MSG; + +rejected: + /* Send SERVICE REJECT */ + LOGMMCTXP(LOGL_NOTICE, ctx, + "Rejecting Service Request with cause '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); + rc = gsm48_tx_gmm_service_rej_oldmsg(msg, reject_cause); + + return rc; + +} + + static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); @@ -1551,6 +1747,9 @@ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, case GSM48_MT_GMM_ATTACH_REQ: rc = gsm48_rx_gmm_att_req(mmctx, msg, llme); break; + case GSM48_MT_GMM_SERVICE_REQ: + rc = gsm48_rx_gmm_service_req(mmctx, msg); + break; /* For all the following types mmctx can not be NULL */ case GSM48_MT_GMM_ID_RESP: if (!mmctx)