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" |
| 25 | |
| 26 | #ifdef BUILD_IU |
| 27 | |
| 28 | #include <gtp.h> |
| 29 | |
| 30 | #include <osmocom/core/rate_ctr.h> |
| 31 | |
| 32 | #include <osmocom/ranap/ranap_common.h> |
| 33 | |
| 34 | #include <osmocom/sgsn/gprs_gmm.h> |
Pau Espin Pedrol | 35f0e66 | 2019-09-02 18:27:27 +0200 | [diff] [blame] | 35 | #include <osmocom/sgsn/gprs_sm.h> |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 36 | #include <osmocom/sgsn/debug.h> |
| 37 | #include <osmocom/sgsn/sgsn.h> |
| 38 | #include <osmocom/sgsn/gprs_ranap.h> |
| 39 | #include <osmocom/sgsn/gprs_gmm_attach.h> |
Pau Espin Pedrol | ccd1252 | 2019-08-30 17:06:36 +0200 | [diff] [blame] | 40 | #include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 41 | |
| 42 | /* Send RAB activation requests for all PDP contexts */ |
| 43 | void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) |
| 44 | { |
| 45 | struct sgsn_pdp_ctx *pdp; |
| 46 | if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) |
| 47 | return; |
| 48 | llist_for_each_entry(pdp, &ctx->pdp_list, list) { |
| 49 | iu_rab_act_ps(pdp->nsapi, pdp); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /* Callback for RAB assignment response */ |
| 54 | static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) |
| 55 | { |
| 56 | uint8_t rab_id; |
| 57 | bool require_pdp_update = false; |
| 58 | struct sgsn_pdp_ctx *pdp = NULL; |
| 59 | RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; |
| 60 | |
| 61 | rab_id = item->rAB_ID.buf[0]; |
| 62 | |
| 63 | pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id); |
| 64 | if (!pdp) { |
| 65 | LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id); |
| 66 | return -1; |
| 67 | } |
| 68 | |
| 69 | if (item->transportLayerAddress) { |
| 70 | LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf, |
| 71 | item->transportLayerAddress->size)); |
| 72 | switch (item->transportLayerAddress->size) { |
| 73 | case 7: |
| 74 | /* It must be IPv4 inside a X213 NSAP */ |
| 75 | memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4); |
| 76 | break; |
| 77 | case 4: |
| 78 | /* It must be a raw IPv4 address */ |
| 79 | memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4); |
| 80 | break; |
| 81 | case 16: |
| 82 | /* TODO: It must be a raw IPv6 address */ |
| 83 | case 19: |
| 84 | /* TODO: It must be IPv6 inside a X213 NSAP */ |
| 85 | default: |
| 86 | LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown " |
| 87 | "transport layer address size %u\n", |
| 88 | item->transportLayerAddress->size); |
| 89 | return -1; |
| 90 | } |
| 91 | require_pdp_update = true; |
| 92 | } |
| 93 | |
| 94 | /* The TEI on the RNC side might have changed, too */ |
| 95 | if (item->iuTransportAssociation && |
| 96 | item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI && |
| 97 | item->iuTransportAssociation->choice.gTP_TEI.buf && |
| 98 | item->iuTransportAssociation->choice.gTP_TEI.size >= 4) { |
| 99 | uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf); |
| 100 | LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n", |
| 101 | pdp->lib->teid_own, tei); |
| 102 | pdp->lib->teid_own = tei; |
| 103 | require_pdp_update = true; |
| 104 | } |
| 105 | |
| 106 | if (require_pdp_update) |
| 107 | gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); |
| 108 | |
| 109 | if (pdp->state != PDP_STATE_CR_CONF) { |
| 110 | send_act_pdp_cont_acc(pdp); |
| 111 | pdp->state = PDP_STATE_CR_CONF; |
| 112 | } |
| 113 | return 0; |
| 114 | |
| 115 | } |
| 116 | |
| 117 | int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data) |
| 118 | { |
| 119 | struct sgsn_mm_ctx *mm; |
| 120 | int rc = -1; |
| 121 | |
| 122 | mm = sgsn_mm_ctx_by_ue_ctx(ctx); |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame^] | 123 | if (!mm) { |
| 124 | LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %d\n", type); |
| 125 | return rc; |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | switch (type) { |
| 129 | case RANAP_IU_EVENT_RAB_ASSIGN: |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 130 | rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data); |
| 131 | break; |
| 132 | case RANAP_IU_EVENT_IU_RELEASE: |
| 133 | /* fall thru */ |
| 134 | case RANAP_IU_EVENT_LINK_INVALIDATED: |
| 135 | /* Clean up ranap_ue_conn_ctx here */ |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame^] | 136 | LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi); |
| 137 | osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 138 | rc = 0; |
| 139 | break; |
| 140 | case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE: |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 141 | /* Continue authentication here */ |
| 142 | mm->iu.ue_ctx->integrity_active = 1; |
Alexander Couzens | 10b3d70 | 2019-09-11 02:31:12 +0200 | [diff] [blame] | 143 | ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 144 | |
| 145 | /* FIXME: remove gmm_authorize */ |
| 146 | if (mm->pending_req != GSM48_MT_GMM_ATTACH_REQ) |
| 147 | gsm48_gmm_authorize(mm); |
| 148 | else |
| 149 | osmo_fsm_inst_dispatch(mm->gmm_att_req.fsm, E_IU_SECURITY_CMD_COMPLETE, NULL); |
| 150 | break; |
| 151 | default: |
Alexander Couzens | 1cb4be9 | 2019-09-10 19:21:31 +0200 | [diff] [blame^] | 152 | LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %i\n", type); |
Pau Espin Pedrol | 6dfb5fe | 2019-08-29 17:21:00 +0200 | [diff] [blame] | 153 | rc = -1; |
| 154 | break; |
| 155 | } |
| 156 | return rc; |
| 157 | } |
| 158 | |
| 159 | int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp) |
| 160 | { |
| 161 | struct msgb *msg; |
| 162 | struct sgsn_mm_ctx *mm = pdp->mm; |
| 163 | struct ranap_ue_conn_ctx *uectx; |
| 164 | uint32_t ggsn_ip; |
| 165 | bool use_x213_nsap; |
| 166 | |
| 167 | uectx = mm->iu.ue_ctx; |
| 168 | use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213); |
| 169 | |
| 170 | /* Get the IP address for ggsn user plane */ |
| 171 | memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l); |
| 172 | ggsn_ip = htonl(ggsn_ip); |
| 173 | |
| 174 | LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x," |
| 175 | " teid_gn=%x, use_x213_nsap=%d\n", |
| 176 | rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); |
| 177 | |
| 178 | msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip, |
| 179 | pdp->lib->teid_gn, use_x213_nsap); |
| 180 | msg->l2h = msg->data; |
| 181 | return ranap_iu_rab_act(uectx, msg); |
| 182 | } |
| 183 | |
| 184 | |
| 185 | /* Main entry point for incoming 04.08 GPRS messages from Iu */ |
| 186 | int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, |
| 187 | uint16_t *sai) |
| 188 | { |
| 189 | struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); |
| 190 | uint8_t pdisc = gsm48_hdr_pdisc(gh); |
| 191 | struct sgsn_mm_ctx *mmctx; |
| 192 | int rc = -EINVAL; |
| 193 | |
| 194 | mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg)); |
| 195 | if (mmctx) { |
| 196 | rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); |
| 197 | if (ra_id) |
| 198 | memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra)); |
| 199 | } |
| 200 | |
| 201 | /* MMCTX can be NULL */ |
| 202 | |
| 203 | switch (pdisc) { |
| 204 | case GSM48_PDISC_MM_GPRS: |
| 205 | rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false); |
| 206 | #pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?" |
| 207 | break; |
| 208 | case GSM48_PDISC_SM_GPRS: |
| 209 | rc = gsm0408_rcv_gsm(mmctx, msg, NULL); |
| 210 | break; |
| 211 | default: |
| 212 | LOGMMCTXP(LOGL_NOTICE, mmctx, |
| 213 | "Unknown GSM 04.08 discriminator 0x%02x: %s\n", |
| 214 | pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); |
| 215 | /* FIXME: return status message */ |
| 216 | break; |
| 217 | } |
| 218 | |
| 219 | /* MMCTX can be invalid */ |
| 220 | |
| 221 | return rc; |
| 222 | } |
| 223 | #endif |