Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 1 | /* Messages on the RANAP interface (Iu mode) */ |
| 2 | |
| 3 | /* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org> |
| 4 | * (C) 2015 by Holger Hans Peter Freyther |
| 5 | * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| 6 | * |
| 7 | * All Rights Reserved |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU Affero General Public License as published by |
| 11 | * the Free Software Foundation; either version 3 of the License, or |
| 12 | * (at your option) any later version. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, |
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | * GNU Affero General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU Affero General Public License |
| 20 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 21 | * |
| 22 | */ |
| 23 | |
| 24 | #include "bscconfig.h" |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 25 | #include <gtp.h> |
| 26 | |
| 27 | #include <osmocom/core/rate_ctr.h> |
Alexander Couzens | afadd10 | 2019-10-08 14:30:59 +0200 | [diff] [blame] | 28 | #include <osmocom/core/tdef.h> |
Alexander Couzens | f23e2db | 2020-07-27 22:39:58 +0200 | [diff] [blame] | 29 | #include <osmocom/gprs/gprs_msgb.h> |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 30 | |
| 31 | #include <osmocom/ranap/ranap_common.h> |
| 32 | |
| 33 | #include <osmocom/sgsn/gprs_gmm.h> |
Pau Espin Pedrol | 35f0e66 | 2019-09-02 18:27:27 +0200 | [diff] [blame] | 34 | #include <osmocom/sgsn/gprs_sm.h> |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 35 | #include <osmocom/sgsn/debug.h> |
| 36 | #include <osmocom/sgsn/sgsn.h> |
| 37 | #include <osmocom/sgsn/gprs_ranap.h> |
| 38 | #include <osmocom/sgsn/gprs_gmm_attach.h> |
Pau Espin Pedrol | ccd1252 | 2019-08-30 17:06:36 +0200 | [diff] [blame] | 39 | #include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 40 | |
| 41 | /* Send RAB activation requests for all PDP contexts */ |
| 42 | void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) |
| 43 | { |
| 44 | struct sgsn_pdp_ctx *pdp; |
| 45 | if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) |
| 46 | return; |
| 47 | llist_for_each_entry(pdp, &ctx->pdp_list, list) { |
| 48 | iu_rab_act_ps(pdp->nsapi, pdp); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | /* Callback for RAB assignment response */ |
| 53 | static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) |
| 54 | { |
| 55 | uint8_t rab_id; |
| 56 | bool require_pdp_update = false; |
| 57 | struct sgsn_pdp_ctx *pdp = NULL; |
| 58 | RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; |
| 59 | |
| 60 | rab_id = item->rAB_ID.buf[0]; |
| 61 | |
| 62 | pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id); |
| 63 | if (!pdp) { |
| 64 | LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id); |
| 65 | return -1; |
| 66 | } |
| 67 | |
| 68 | if (item->transportLayerAddress) { |
| 69 | LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf, |
| 70 | item->transportLayerAddress->size)); |
| 71 | switch (item->transportLayerAddress->size) { |
| 72 | case 7: |
| 73 | /* It must be IPv4 inside a X213 NSAP */ |
| 74 | memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4); |
| 75 | break; |
| 76 | case 4: |
| 77 | /* It must be a raw IPv4 address */ |
| 78 | memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4); |
| 79 | break; |
| 80 | case 16: |
| 81 | /* TODO: It must be a raw IPv6 address */ |
| 82 | case 19: |
| 83 | /* TODO: It must be IPv6 inside a X213 NSAP */ |
| 84 | default: |
| 85 | LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown " |
| 86 | "transport layer address size %u\n", |
| 87 | item->transportLayerAddress->size); |
| 88 | return -1; |
| 89 | } |
| 90 | require_pdp_update = true; |
| 91 | } |
| 92 | |
| 93 | /* The TEI on the RNC side might have changed, too */ |
| 94 | if (item->iuTransportAssociation && |
| 95 | item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI && |
| 96 | item->iuTransportAssociation->choice.gTP_TEI.buf && |
| 97 | item->iuTransportAssociation->choice.gTP_TEI.size >= 4) { |
| 98 | uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf); |
| 99 | LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n", |
| 100 | pdp->lib->teid_own, tei); |
| 101 | pdp->lib->teid_own = tei; |
| 102 | require_pdp_update = true; |
| 103 | } |
| 104 | |
| 105 | if (require_pdp_update) |
| 106 | gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); |
| 107 | |
| 108 | if (pdp->state != PDP_STATE_CR_CONF) { |
| 109 | send_act_pdp_cont_acc(pdp); |
| 110 | pdp->state = PDP_STATE_CR_CONF; |
| 111 | } |
| 112 | return 0; |
| 113 | |
| 114 | } |
| 115 | |
| 116 | int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data) |
| 117 | { |
| 118 | struct sgsn_mm_ctx *mm; |
| 119 | int rc = -1; |
| 120 | |
| 121 | mm = sgsn_mm_ctx_by_ue_ctx(ctx); |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame] | 122 | if (!mm) { |
Pau Espin Pedrol | 183e6c3 | 2021-03-25 15:54:45 +0100 | [diff] [blame^] | 123 | LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %s\n", |
| 124 | ranap_iu_event_type_str(type)); |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 125 | ranap_iu_free_ue(ctx); |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame] | 126 | return rc; |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | switch (type) { |
| 130 | case RANAP_IU_EVENT_RAB_ASSIGN: |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 131 | rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data); |
| 132 | break; |
| 133 | case RANAP_IU_EVENT_IU_RELEASE: |
| 134 | /* fall thru */ |
| 135 | case RANAP_IU_EVENT_LINK_INVALIDATED: |
| 136 | /* Clean up ranap_ue_conn_ctx here */ |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame] | 137 | LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi); |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 138 | if (mm->iu.mm_state_fsm->state == ST_PMM_CONNECTED) |
| 139 | osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL); |
| 140 | else |
| 141 | sgsn_ranap_iu_free(mm); |
| 142 | |
Alexander Couzens | 62f6f9a | 2019-09-11 02:44:06 +0200 | [diff] [blame] | 143 | /* TODO: move this into FSM */ |
| 144 | if (mm->ran_type == MM_CTX_T_UTRAN_Iu && mm->gmm_att_req.fsm->state != ST_INIT) |
| 145 | osmo_fsm_inst_dispatch(mm->gmm_att_req.fsm, E_REJECT, (void *) GMM_DISCARD_MS_WITHOUT_REJECT); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 146 | rc = 0; |
| 147 | break; |
| 148 | case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE: |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 149 | /* Continue authentication here */ |
| 150 | mm->iu.ue_ctx->integrity_active = 1; |
Alexander Couzens | 10b3d70 | 2019-09-11 02:31:12 +0200 | [diff] [blame] | 151 | ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 152 | |
| 153 | /* FIXME: remove gmm_authorize */ |
| 154 | if (mm->pending_req != GSM48_MT_GMM_ATTACH_REQ) |
| 155 | gsm48_gmm_authorize(mm); |
| 156 | else |
| 157 | osmo_fsm_inst_dispatch(mm->gmm_att_req.fsm, E_IU_SECURITY_CMD_COMPLETE, NULL); |
Alexander Couzens | 743e687 | 2019-10-08 12:40:50 +0200 | [diff] [blame] | 158 | rc = 0; |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 159 | break; |
| 160 | default: |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame] | 161 | LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %i\n", type); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 162 | rc = -1; |
| 163 | break; |
| 164 | } |
| 165 | return rc; |
| 166 | } |
| 167 | |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 168 | void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx) |
| 169 | { |
| 170 | if (!ctx) |
| 171 | return; |
| 172 | |
| 173 | if (!ctx->iu.ue_ctx) |
| 174 | return; |
| 175 | |
| 176 | ranap_iu_free_ue(ctx->iu.ue_ctx); |
| 177 | ctx->iu.ue_ctx = NULL; |
| 178 | } |
| 179 | |
| 180 | void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx, |
| 181 | const struct RANAP_Cause *cause) |
| 182 | { |
Alexander Couzens | afadd10 | 2019-10-08 14:30:59 +0200 | [diff] [blame] | 183 | unsigned long X1001; |
| 184 | |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 185 | if (!ctx) |
| 186 | return; |
| 187 | |
| 188 | if (!ctx->iu.ue_ctx) |
| 189 | return; |
| 190 | |
Alexander Couzens | afadd10 | 2019-10-08 14:30:59 +0200 | [diff] [blame] | 191 | X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1); |
| 192 | |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 193 | ranap_iu_tx_release_free(ctx->iu.ue_ctx, |
| 194 | cause, |
Alexander Couzens | afadd10 | 2019-10-08 14:30:59 +0200 | [diff] [blame] | 195 | (int) X1001); |
Alexander Couzens | eb5aee5 | 2019-09-10 21:00:18 +0200 | [diff] [blame] | 196 | ctx->iu.ue_ctx = NULL; |
| 197 | } |
| 198 | |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 199 | int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp) |
| 200 | { |
| 201 | struct msgb *msg; |
| 202 | struct sgsn_mm_ctx *mm = pdp->mm; |
| 203 | struct ranap_ue_conn_ctx *uectx; |
| 204 | uint32_t ggsn_ip; |
| 205 | bool use_x213_nsap; |
| 206 | |
| 207 | uectx = mm->iu.ue_ctx; |
| 208 | use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213); |
| 209 | |
| 210 | /* Get the IP address for ggsn user plane */ |
| 211 | memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l); |
| 212 | ggsn_ip = htonl(ggsn_ip); |
| 213 | |
| 214 | LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x," |
| 215 | " teid_gn=%x, use_x213_nsap=%d\n", |
| 216 | rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); |
| 217 | |
| 218 | msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip, |
| 219 | pdp->lib->teid_gn, use_x213_nsap); |
| 220 | msg->l2h = msg->data; |
| 221 | return ranap_iu_rab_act(uectx, msg); |
| 222 | } |
| 223 | |
| 224 | |
| 225 | /* Main entry point for incoming 04.08 GPRS messages from Iu */ |
| 226 | int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, |
| 227 | uint16_t *sai) |
| 228 | { |
| 229 | struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); |
| 230 | uint8_t pdisc = gsm48_hdr_pdisc(gh); |
| 231 | struct sgsn_mm_ctx *mmctx; |
| 232 | int rc = -EINVAL; |
| 233 | |
| 234 | mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg)); |
| 235 | if (mmctx) { |
| 236 | rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); |
| 237 | if (ra_id) |
| 238 | memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra)); |
| 239 | } |
| 240 | |
| 241 | /* MMCTX can be NULL */ |
| 242 | |
| 243 | switch (pdisc) { |
| 244 | case GSM48_PDISC_MM_GPRS: |
| 245 | rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false); |
| 246 | #pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?" |
| 247 | break; |
| 248 | case GSM48_PDISC_SM_GPRS: |
| 249 | rc = gsm0408_rcv_gsm(mmctx, msg, NULL); |
| 250 | break; |
| 251 | default: |
| 252 | LOGMMCTXP(LOGL_NOTICE, mmctx, |
| 253 | "Unknown GSM 04.08 discriminator 0x%02x: %s\n", |
| 254 | pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); |
| 255 | /* FIXME: return status message */ |
| 256 | break; |
| 257 | } |
| 258 | |
| 259 | /* MMCTX can be invalid */ |
| 260 | |
| 261 | return rc; |
| 262 | } |