1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/*******************************************************************************
Eurecom OpenAirInterface
Copyright(c) 1999 - 2013 Eurecom
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Contact Information
Openair Admin: openair_admin@eurecom.fr
Openair Tech : openair_tech@eurecom.fr
Forums : http://forums.eurecom.fr/openairinterface
Address : EURECOM, Campus SophiaTech, 450 Route des Chappes
06410 Biot FRANCE
*******************************************************************************/
#include <stdint.h>
#include "s1ap_common.h"
#include "s1ap_ies_defs.h"
#include "s1ap_mme_encoder.h"
#include "s1ap_mme_handlers.h"
#include "s1ap_mme_nas_procedures.h"
#include "s1ap_mme_retransmission.h"
#include "s1ap_mme_itti_messaging.h"
#include "s1ap_mme.h"
#include "intertask_interface.h"
#include "timer.h"
#include "assertions.h"
#include "conversions.h"
/* Every time a new UE is associated, increment this variable.
* But care if it wraps to increment also the mme_ue_s1ap_id_has_wrapped
* variable. Limit: UINT32_MAX (in stdint.h).
*/
static uint32_t mme_ue_s1ap_id = 0;
static uint8_t mme_ue_s1ap_id_has_wrapped = 0;
int s1ap_mme_handle_initial_ue_message(uint32_t assoc_id, uint32_t stream,
struct s1ap_message_s *message)
{
S1ap_InitialUEMessageIEs_t *initialUEMessage_p;
ue_description_t *ue_ref;
eNB_description_t *eNB_ref;
uint32_t eNB_ue_s1ap_id;
initialUEMessage_p = &message->msg.s1ap_InitialUEMessageIEs;
if ((eNB_ref = s1ap_is_eNB_assoc_id_in_list(assoc_id)) == NULL) {
S1AP_DEBUG("Unkwnon eNB on assoc_id %d\n", assoc_id);
return -1;
}
// eNB UE S1AP ID is limited to 24 bits
eNB_ue_s1ap_id = (uint32_t)(initialUEMessage_p->eNB_UE_S1AP_ID & 0x00ffffff);
S1AP_DEBUG("New Initial UE message received with eNB UE S1AP ID: 0x%06x\n",
eNB_ue_s1ap_id);
if ((ue_ref = s1ap_is_ue_eNB_id_in_list(eNB_ref, eNB_ue_s1ap_id)) == NULL) {
/* This UE eNB Id has currently no known s1 association.
* Create new UE context by associating new mme_ue_s1ap_id.
* Update eNB UE list.
* Forward message to NAS.
*/
if ((ue_ref = s1ap_new_ue(assoc_id)) == NULL)
// If we failed to allocate a new UE return -1
{
return -1;
}
ue_ref->s1_ue_state = S1AP_UE_WAITING_CSR;
ue_ref->eNB_ue_s1ap_id = eNB_ue_s1ap_id;
ue_ref->mme_ue_s1ap_id = (uint32_t)ue_ref;
// On which stream we received the message
ue_ref->sctp_stream_recv = stream;
ue_ref->sctp_stream_send = ue_ref->eNB->next_sctp_stream;
/* Increment the sctp stream for the eNB association.
* If the next sctp stream is >= outstream negociated between eNB and MME,
* wrap to first stream.
* TODO: search for the first available stream instead.
*/
if (ue_ref->eNB->next_sctp_stream++ >= ue_ref->eNB->outstreams) {
ue_ref->eNB->next_sctp_stream = 1;
}
/* Increment the unique UE mme s1ap id and
* take care about overflow case.
*/
if (mme_ue_s1ap_id_has_wrapped == 0) {
mme_ue_s1ap_id++;
if (mme_ue_s1ap_id == 0) {
mme_ue_s1ap_id_has_wrapped = 1;
}
} else {
/* TODO: should take the first available mme_ue_s1ap_id instead of
* the mme_ue_s1ap_id variable.
*/
assert(0);
}
s1ap_dump_eNB(ue_ref->eNB);
{
/* We received the first NAS transport message: initial UE message.
* The containt of the NAS pdu should be forwarded to NAS for processing
*/
MessageDef *message_p;
nas_establish_ind_t *con_ind_p;
s1ap_initial_ue_message_t *s1ap_p;
message_p = itti_alloc_new_message(TASK_S1AP, NAS_CONNECTION_ESTABLISHMENT_IND);
/* We failed to allocate a new message... */
if (message_p == NULL) {
return -1;
}
con_ind_p = &message_p->msg.nas_conn_est_ind.nas;
s1ap_p = &message_p->msg.nas_conn_est_ind.transparent;
s1ap_p->eNB_ue_s1ap_id = eNB_ue_s1ap_id;
s1ap_p->mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
#if !defined(DISABLE_USE_NAS)
con_ind_p->UEid = ue_ref->mme_ue_s1ap_id;
#endif
BIT_STRING_TO_CELL_IDENTITY(&initialUEMessage_p->eutran_cgi.cell_ID,
s1ap_p->e_utran_cgi.cell_identity);
TBCD_TO_PLMN_T(&initialUEMessage_p->eutran_cgi.pLMNidentity,
&s1ap_p->e_utran_cgi.plmn);
#if !defined(DISABLE_USE_NAS)
/* Copy the TAI */
// TBCD_TO_PLMN_T(&initialUEMessage_p->tai.pLMNidentity, &con_ind_p->tai.plmn);
OCTET_STRING_TO_INT16(&initialUEMessage_p->tai.tAC, con_ind_p->tac);
#else
/* TODO: copy the TAC */
// TBCD_TO_PLMN_T(&initialUEMessage_p->tai.pLMNidentity, &con_ind_p->tai.plmn);
OCTET_STRING_TO_INT16(&initialUEMessage_p->tai.tAC, con_ind_p->tac);
#endif
/* Copy the NAS payload */
con_ind_p->initialNasMsg.length = initialUEMessage_p->nas_pdu.size;
con_ind_p->initialNasMsg.data = malloc(sizeof(con_ind_p->initialNasMsg.data) *
initialUEMessage_p->nas_pdu.size);
memcpy(con_ind_p->initialNasMsg.data, initialUEMessage_p->nas_pdu.buf,
initialUEMessage_p->nas_pdu.size);
return itti_send_msg_to_task(TASK_NAS, INSTANCE_DEFAULT, message_p);
}
}
return 0;
}
int s1ap_mme_handle_uplink_nas_transport(uint32_t assoc_id, uint32_t stream,
struct s1ap_message_s *message)
{
S1ap_UplinkNASTransportIEs_t *uplinkNASTransport_p;
ue_description_t *ue_ref;
uplinkNASTransport_p = &message->msg.s1ap_UplinkNASTransportIEs;
if ((ue_ref = s1ap_is_ue_mme_id_in_list(uplinkNASTransport_p->mme_ue_s1ap_id))
== NULL) {
S1AP_DEBUG("No UE is attached to this mme UE s1ap id: %d\n",
(int)uplinkNASTransport_p->mme_ue_s1ap_id);
return -1;
}
if (ue_ref->s1_ue_state != S1AP_UE_CONNECTED) {
S1AP_DEBUG("Received uplink NAS transport while UE in state != S1AP_UE_CONNECTED\n");
return -1;
}
//TODO: forward NAS PDU to NAS
DevMessage("TODO: forward NAS PDU to NAS\n");
return 0;
}
int s1ap_mme_handle_nas_non_delivery(uint32_t assoc_id, uint32_t stream,
struct s1ap_message_s *message)
{
/* UE associated signalling on stream == 0 is not valid. */
if (stream == 0) {
S1AP_DEBUG("Received new nas non delivery on stream == 0\n");
return -1;
}
//TODO: forward NAS PDU to NAS
DevMessage("TODO: forward NAS PDU to NAS\n");
return 0;
}
int s1ap_generate_downlink_nas_transport(nas_dl_data_ind_t *nas_dl_data_ind)
{
ue_description_t *ue_ref;
uint8_t *buffer_p;
uint32_t length;
DevAssert(nas_dl_data_ind != NULL);
if ((ue_ref = s1ap_is_ue_mme_id_in_list(nas_dl_data_ind->UEid)) == NULL) {
/* If the UE-associated logical S1-connection is not established,
* the MME shall allocate a unique MME UE S1AP ID to be used for the UE.
*/
DevMessage("This case is not handled right now\n");
} else {
/* We have fount the UE in the list.
* Create new IE list message and encode it.
*/
S1ap_DownlinkNASTransportIEs_t *downlinkNasTransport;
s1ap_message message;
memset(&message, 0, sizeof(s1ap_message));
message.procedureCode = S1ap_ProcedureCode_id_downlinkNASTransport;
message.direction = S1AP_PDU_PR_initiatingMessage;
downlinkNasTransport = &message.msg.s1ap_DownlinkNASTransportIEs;
/* Setting UE informations with the ones fount in ue_ref */
downlinkNasTransport->mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
downlinkNasTransport->eNB_UE_S1AP_ID = ue_ref->eNB_ue_s1ap_id;
OCTET_STRING_fromBuf(&downlinkNasTransport->nas_pdu,
(char*)nas_dl_data_ind->nasMsg.data,
nas_dl_data_ind->nasMsg.length);
if (s1ap_mme_encode_pdu(&message, &buffer_p, &length) < 0) {
// TODO: handle something
return -1;
}
return s1ap_mme_itti_send_sctp_request(buffer_p, length,
ue_ref->eNB->sctp_assoc_id,
ue_ref->sctp_stream_send);
}
return 0;
}
int s1ap_handle_attach_accepted(nas_attach_accept_t *attach_accept_p)
{
/* We received create session response from S-GW on S11 interface abstraction.
* At least one bearer has been established. We can now send s1ap initial context setup request
* message to eNB.
*/
char supportedAlgorithms[] = { 0x02, 0xa0 };
uint8_t offset = 0;
uint8_t *buffer_p;
uint32_t length;
ue_description_t *ue_ref = NULL;
s1ap_message message;
s1ap_initial_ctxt_setup_req_t *initial_p;
S1ap_InitialContextSetupRequestIEs_t *initialContextSetupRequest_p;
S1ap_E_RABToBeSetupItemCtxtSUReq_t e_RABToBeSetup;
DevAssert(attach_accept_p != NULL);
initial_p = &attach_accept_p->transparent;
if ((ue_ref = s1ap_is_ue_mme_id_in_list(initial_p->mme_ue_s1ap_id)) == NULL) {
S1AP_DEBUG("This mme ue s1ap id (%08x) is not attached to any UE context\n",
initial_p->mme_ue_s1ap_id);
return -1;
}
/* Start the outcome response timer.
* When time is reached, MME consider that procedure outcome has failed.
*/
// timer_setup(mme_config.s1ap_config.outcome_drop_timer_sec, 0, TASK_S1AP, INSTANCE_DEFAULT,
// TIMER_ONE_SHOT,
// NULL,
// &ue_ref->outcome_response_timer_id);
/* Insert the timer in the MAP of mme_ue_s1ap_id <-> timer_id */
// s1ap_timer_insert(ue_ref->mme_ue_s1ap_id, ue_ref->outcome_response_timer_id);
memset(&message, 0, sizeof(s1ap_message));
memset(&e_RABToBeSetup, 0, sizeof(S1ap_E_RABToBeSetupItemCtxtSUReq_t));
message.procedureCode = S1ap_ProcedureCode_id_InitialContextSetup;
message.direction = S1AP_PDU_PR_initiatingMessage;
initialContextSetupRequest_p = &message.msg.s1ap_InitialContextSetupRequestIEs;
initialContextSetupRequest_p->mme_ue_s1ap_id = (unsigned long)ue_ref->mme_ue_s1ap_id;
initialContextSetupRequest_p->eNB_UE_S1AP_ID = (unsigned long)ue_ref->eNB_ue_s1ap_id;
/* uEaggregateMaximumBitrateDL and uEaggregateMaximumBitrateUL expressed in term of bits/sec */
// asn_int642INTEGER(
// &initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL,
// initial_p->ambr.br_dl);
// asn_int642INTEGER(
// &initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL,
// initial_p->ambr.br_ul);
initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL = initial_p->ambr.br_dl;
initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL = initial_p->ambr.br_ul;
e_RABToBeSetup.e_RAB_ID = initial_p->ebi;
e_RABToBeSetup.e_RABlevelQoSParameters.qCI = initial_p->qci;
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel
= initial_p->prio_level; //No priority
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability
= initial_p->pre_emp_capability;
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability
= initial_p->pre_emp_vulnerability;
/* Set the GTP-TEID. This is the S1-U S-GW TEID */
INT32_TO_OCTET_STRING(initial_p->teid, &e_RABToBeSetup.gTP_TEID);
/* S-GW IP address(es) for user-plane */
if ((initial_p->s_gw_address.pdn_type == IPv4) ||
(initial_p->s_gw_address.pdn_type == IPv4_AND_v6))
{
e_RABToBeSetup.transportLayerAddress.buf = calloc(4, sizeof(uint8_t));
/* Only IPv4 supported */
memcpy(e_RABToBeSetup.transportLayerAddress.buf,
initial_p->s_gw_address.address.ipv4_address,
4);
offset += 4;
e_RABToBeSetup.transportLayerAddress.size = 4;
e_RABToBeSetup.transportLayerAddress.bits_unused = 0;
}
if ((initial_p->s_gw_address.pdn_type == IPv6) ||
(initial_p->s_gw_address.pdn_type == IPv4_AND_v6))
{
if (offset == 0) {
/* Both IPv4 and IPv6 provided */
/* TODO: check memory allocation */
e_RABToBeSetup.transportLayerAddress.buf = calloc(16, sizeof(uint8_t));
} else {
/* Only IPv6 supported */
/* TODO: check memory allocation */
e_RABToBeSetup.transportLayerAddress.buf
= realloc(e_RABToBeSetup.transportLayerAddress.buf, (16 + offset) * sizeof(uint8_t));
}
memcpy(&e_RABToBeSetup.transportLayerAddress.buf[offset],
initial_p->s_gw_address.address.ipv6_address,
16);
e_RABToBeSetup.transportLayerAddress.size = 16 + offset;
e_RABToBeSetup.transportLayerAddress.bits_unused = 0;
}
ASN_SEQUENCE_ADD(&initialContextSetupRequest_p->e_RABToBeSetupListCtxtSUReq,
&e_RABToBeSetup);
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.buf =
(uint8_t *)supportedAlgorithms;
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.size = 2;
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.bits_unused
= 0;
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.buf
= (uint8_t *)supportedAlgorithms;
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.size
= 2;
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.bits_unused
= 0;
initialContextSetupRequest_p->securityKey.buf = initial_p->keNB; /* 256 bits length */
initialContextSetupRequest_p->securityKey.size = 32;
initialContextSetupRequest_p->securityKey.bits_unused = 0;
if (s1ap_mme_encode_pdu(&message, &buffer_p, &length) < 0) {
// TODO: handle something
return -1;
}
return s1ap_mme_itti_send_sctp_request(buffer_p, length, ue_ref->eNB->sctp_assoc_id,
ue_ref->sctp_stream_send);
}