blob: 16a6f5e0863e250c7e2c44d4b138ed8a17d654a0 [file] [log] [blame]
Harald Welte9fe1f9f2018-11-29 13:47:39 +01001/*! \file iu_up.c
2 * IuUP (Iu User Plane) according to 3GPP TS 25.415 */
3/*
4 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <errno.h>
20#include <inttypes.h>
21
22#include <osmocom/core/crc8gen.h>
23#include <osmocom/core/crc16gen.h>
24#include <osmocom/core/fsm.h>
25#include <osmocom/core/prim.h>
26#include <osmocom/core/timer.h>
27#include <osmocom/core/logging.h>
28
29#include <osmocom/gsm/prim.h>
30#include <osmocom/gsm/protocol/gsm_25_415.h>
31#include <osmocom/gsm/iuup.h>
32
33/***********************************************************************
34 * CRC Calculation
35 ***********************************************************************/
36
37/* Section 6.6.3.8 Header CRC */
38const struct osmo_crc8gen_code iuup_hdr_crc_code = {
39 .bits = 6,
40 .poly = 47,
41 .init = 0,
42 .remainder = 0,
43};
44
45/* Section 6.6.3.9 Payload CRC */
46const struct osmo_crc16gen_code iuup_data_crc_code = {
47 .bits = 10,
48 .poly = 563,
49 .init = 0,
50 .remainder = 0,
51};
52
53static int iuup_get_payload_offset(const uint8_t *iuup_pdu)
54{
55 uint8_t pdu_type = iuup_pdu[0] >> 4;
56 switch (pdu_type) {
57 case 0:
58 case 14:
59 return 4;
60 case 1:
61 return 3;
62 default:
63 return -1;
64 }
65}
66
67int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
68{
69 ubit_t buf[1024*8];
70 uint8_t pdu_type;
71 int offset, payload_len_bytes;
72
73 if (pdu_len < 1)
74 return -1;
75
76 pdu_type = iuup_pdu[0] >> 4;
77
78 /* Type 1 has no CRC */
79 if (pdu_type == 1)
80 return 0;
81
82 offset = iuup_get_payload_offset(iuup_pdu);
83 if (offset < 0)
84 return offset;
85
86 if (pdu_len < offset)
87 return -1;
88
89 payload_len_bytes = pdu_len - offset;
90 osmo_pbit2ubit(buf, iuup_pdu+offset, payload_len_bytes*8);
91 return osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, payload_len_bytes*8);
92}
93
94int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
95{
96 ubit_t buf[2*8];
97
98 if (pdu_len < 2)
99 return -1;
100
101 osmo_pbit2ubit(buf, iuup_pdu, 2*8);
102 return osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
103}
104
105/***********************************************************************
106 * Internal State / FSM (Annex B)
107 ***********************************************************************/
108
109#define S(x) (1 << (x))
110
111#define IUUP_TIMER_INIT 1
112#define IUUP_TIMER_TA 2
113#define IUUP_TIMER_RC 3
114
115struct osmo_timer_nt {
116 uint32_t n; /* number of repetitions */
117 struct osmo_iuup_tnl_prim *retrans_itp;
118 struct osmo_timer_list timer;
119};
120
121struct osmo_iuup_instance {
122 struct osmo_iuup_rnl_config config;
123 struct osmo_fsm_inst *fi;
124
125 uint8_t mode_version;
126
127 struct {
128 struct osmo_timer_nt init;
129 struct osmo_timer_nt ta;
130 struct osmo_timer_nt rc;
131 } timer;
132 /* call-back function to pass primitives up to the user */
133 osmo_prim_cb user_prim_cb;
134 void *user_prim_priv;
135 osmo_prim_cb transport_prim_cb;
136 void *transport_prim_priv;
137 uint8_t type14_fn; /* 2 bits */
138};
139
140enum iuup_fsm_state {
141 IUUP_FSM_ST_NULL,
142 IUUP_FSM_ST_INIT,
143 IUUP_FSM_ST_TrM_DATA_XFER_READY,
144 IUUP_FSM_ST_SMpSDU_DATA_XFER_READY,
145};
146
147enum iuup_fsm_event {
148 IUUP_FSM_EVT_IUUP_CONFIG_REQ,
149 IUUP_FSM_EVT_IUUP_DATA_REQ,
150 IUUP_FSM_EVT_IUUP_DATA_IND,
151 IUUP_FSM_EVT_IUUP_STATUS_REQ,
152 IUUP_FSM_EVT_IUUP_STATUS_IND,
153 IUUP_FSM_EVT_SSASAR_UNITDATA_REQ,
154 IUUP_FSM_EVT_SSASAR_UNITDATA_IND,
155 IUUP_FSM_EVT_IUUP_UNITDATA_REQ,
156 IUUP_FSM_EVT_IUUP_UNITDATA_IND,
157 IUUP_FSM_EVT_INIT,
158 IUUP_FSM_EVT_LAST_INIT_ACK,
159 IUUP_FSM_EVT_INIT_NACK,
160};
161
162static const struct value_string iuup_fsm_event_names[] = {
Philipp Maierbdd7df32022-01-18 11:58:18 +0100163 { IUUP_FSM_EVT_IUUP_CONFIG_REQ, "IuUP-CONFIG-req" },
164 { IUUP_FSM_EVT_IUUP_DATA_REQ, "IuUP-DATA-req" },
165 { IUUP_FSM_EVT_IUUP_DATA_IND, "IuUP-DATA-ind" },
166 { IUUP_FSM_EVT_IUUP_STATUS_REQ, "IuUP-STATUS-req" },
167 { IUUP_FSM_EVT_IUUP_STATUS_IND, "IuUP-STATUS-ind" },
168 { IUUP_FSM_EVT_SSASAR_UNITDATA_REQ, "SSSAR-UNITDATA-req" },
169 { IUUP_FSM_EVT_SSASAR_UNITDATA_IND, "SSSAR-UNITDATA-ind" },
170 { IUUP_FSM_EVT_IUUP_UNITDATA_REQ, "IuUP-UNITDATA-req" },
171 { IUUP_FSM_EVT_IUUP_UNITDATA_IND, "IuUP-UNITDATA-ind" },
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100172 { IUUP_FSM_EVT_INIT, "INIT" },
173 { IUUP_FSM_EVT_LAST_INIT_ACK, "LAST_INIT_ACK" },
174 { IUUP_FSM_EVT_INIT_NACK, "INIT_NACK" },
175 { 0, NULL }
176};
177
178static inline uint8_t iuup_get_pdu_type(const uint8_t *data)
179{
180 return data[0] >> 4;
181}
182
183static inline uint8_t iuup_get_hdr_crc(const uint8_t *data)
184{
185 return data[2] >> 2;
186}
187
188/* Helper functions to store non-packed structs in msgb so that pointers are properly aligned: */
189#define IUUP_MSGB_SIZE 4096
190#define PTR_ALIGNMENT_BYTES 8
191#define IUUP_MSGB_HEADROOM_MIN_REQUIRED (OSMO_MAX(sizeof(struct osmo_iuup_tnl_prim), sizeof(struct osmo_iuup_rnl_prim)) + (PTR_ALIGNMENT_BYTES - 1))
192static inline struct msgb *osmo_iuup_msgb_alloc_c(void *ctx, size_t size)
193{
Vadim Yanitskiy71845112023-02-24 18:27:29 +0700194 OSMO_ASSERT(size > IUUP_MSGB_HEADROOM_MIN_REQUIRED);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100195 return msgb_alloc_headroom_c(ctx, size, IUUP_MSGB_HEADROOM_MIN_REQUIRED, "iuup-msgb");
196}
197
198/* push data so that the resulting pointer to write to is aligned to 8 byte */
199static inline __attribute__((assume_aligned(PTR_ALIGNMENT_BYTES)))
200unsigned char *aligned_msgb_push(struct msgb *msg, unsigned int len)
201{
202 uint8_t *ptr = (msgb_data(msg) - len);
203 size_t extra_size = ((uintptr_t)ptr & (PTR_ALIGNMENT_BYTES - 1));
204
205 return msgb_push(msg, len + extra_size);
206}
207
208struct osmo_iuup_rnl_prim *osmo_iuup_rnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
209{
210 struct msgb *msg;
211 struct osmo_iuup_rnl_prim *irp;
212
213 msg = osmo_iuup_msgb_alloc_c(ctx, size);
214 irp = (struct osmo_iuup_rnl_prim *)aligned_msgb_push(msg, sizeof(*irp));
215 osmo_prim_init(&irp->oph, SAP_IUUP_RNL, primitive, operation, msg);
216 return irp;
217}
218
219struct osmo_iuup_tnl_prim *osmo_iuup_tnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
220{
221 struct msgb *msg;
222 struct osmo_iuup_tnl_prim *itp;
223
224 msg = osmo_iuup_msgb_alloc_c(ctx, size);
225 itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
226 osmo_prim_init(&itp->oph, SAP_IUUP_TNL, primitive, operation, msg);
227 return itp;
228}
229
230/* 6.6.2.3.2 */
231static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, uint8_t fn)
232{
233 struct osmo_iuup_tnl_prim *itp;
234 struct iuup_ctrl_ack *ack;
235 itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
236 itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_ack));
237 ack = (struct iuup_ctrl_ack *) msgb_l2(itp->oph.msg);
238 *ack = (struct iuup_ctrl_ack){
239 .hdr = {
240 .frame_nr = fn,
241 .ack_nack = IUUP_AN_ACK,
242 .pdu_type = IUUP_PDU_T_CONTROL,
243 .proc_ind = proc_ind,
244 .mode_version = iui->mode_version,
245 .payload_crc_hi = 0,
246 .header_crc = 0,
247 .payload_crc_lo = 0,
248 },
249 };
250 ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
251 return itp;
252}
253
254/* 6.6.2.3.3 */
255static struct osmo_iuup_tnl_prim *tnp_ctrl_nack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
256{
257 struct osmo_iuup_tnl_prim *itp;
258 struct iuup_ctrl_nack *nack;
259 itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
260 itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_nack));
261 nack = (struct iuup_ctrl_nack *) msgb_l2(itp->oph.msg);
262 *nack = (struct iuup_ctrl_nack){
263 .hdr = {
264 .frame_nr = fn,
265 .ack_nack = IUUP_AN_NACK,
266 .pdu_type = IUUP_PDU_T_CONTROL,
267 .proc_ind = proc_ind,
268 .mode_version = iui->mode_version,
269 .payload_crc_hi = 0,
270 .header_crc = 0,
271 .payload_crc_lo = 0,
272 },
273 .spare = 0,
274 .error_cause = error_cause,
275 };
276 nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
277 return itp;
278}
279
280/* 6.6.2.3.4.1 */
281static struct osmo_iuup_tnl_prim *tnp_ctrl_init_alloc(struct osmo_iuup_instance *iui)
282{
283 struct osmo_iuup_tnl_prim *itp;
284 struct iuup_pdutype14_hdr *hdr;
285 struct iuup_ctrl_init_hdr *ihdr;
286 struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
287 struct iuup_ctrl_init_tail *itail;
288 unsigned int i, j;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100289 uint16_t payload_crc;
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200290 uint8_t rfci_cnt;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100291 struct msgb *msg;
292
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100293 itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
294 msg = itp->oph.msg;
295
296 msg->l2h = msgb_put(msg, sizeof(*hdr));
297 hdr = (struct iuup_pdutype14_hdr *)msgb_l2(msg);
298 hdr->frame_nr = iui->type14_fn++;
299 hdr->ack_nack = IUUP_AN_PROCEDURE;
300 hdr->pdu_type = IUUP_PDU_T_CONTROL;
301 hdr->proc_ind = IUUP_PROC_INIT;
302 hdr->mode_version = 0; /* Use here the minimum version required to negotiate */
303 hdr->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
304
305 ihdr = (struct iuup_ctrl_init_hdr *)msgb_put(msg, sizeof(*ihdr));
306 ihdr->chain_ind = 0; /* this frame is the last frame for the procedure. TODO: support several */
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200307 ihdr->num_subflows_per_rfci = iui->config.num_subflows;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100308 ihdr->ti = iui->config.IPTIs_present ? 1 : 0;
309 ihdr->spare = 0;
310
311 /* RFCI + subflow size part: */
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200312 rfci_cnt = 0;
313 for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
314 bool last;
315 uint8_t len_size;
316 struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
317 if (!rfci->used)
318 continue;
319 rfci_cnt++;
320 last = (rfci_cnt == iui->config.num_rfci);
321
322 len_size = 1;
323 for (j = 0; j < iui->config.num_subflows; j++) {
324 if (rfci->subflow_sizes[j] > UINT8_MAX)
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100325 len_size = 2;
326 }
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200327
328 ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)msgb_put(msg, sizeof(*ihdr_rfci) + len_size * iui->config.num_subflows);
329 ihdr_rfci->rfci = rfci->id;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100330 ihdr_rfci->li = len_size - 1;
331 ihdr_rfci->lri = last;
332 if (len_size == 2) {
333 uint16_t *buf = (uint16_t *)&ihdr_rfci->subflow_length[0];
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200334 for (j = 0; j < iui->config.num_subflows; j++)
335 osmo_store16be(rfci->subflow_sizes[j], buf++);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100336 } else {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200337 for (j = 0; j < iui->config.num_subflows; j++)
338 ihdr_rfci->subflow_length[j] = rfci->subflow_sizes[j];
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100339 }
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200340 /* early loop termination: */
341 if (last)
342 break;
343 }
344 /* Sanity check: */
345 if (rfci_cnt != iui->config.num_rfci) {
346 LOGP(DLIUUP, LOGL_ERROR, "rfci_cnt %u != num_rfci %u\n",
347 rfci_cnt, iui->config.num_rfci);
348 msgb_free(msg);
349 return NULL;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100350 }
351
352 if (iui->config.IPTIs_present) {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200353 uint8_t num_bytes = (iui->config.num_rfci + 1) / 2;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100354 uint8_t *buf = msgb_put(msg, num_bytes);
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200355 rfci_cnt = 0;
356 for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
357 struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
358 if (!rfci->used)
359 continue;
360 if (!(rfci_cnt & 0x01)) /* is even: */
361 buf[rfci_cnt / 2] = (((uint8_t)rfci->IPTI) << 4);
362 else
363 buf[rfci_cnt / 2] |= (rfci->IPTI & 0x0F);
364 rfci_cnt++;
365 /* early loop termination: */
366 if (rfci_cnt == iui->config.num_rfci)
367 break;
368 }
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100369 }
370
371 itail = (struct iuup_ctrl_init_tail *)msgb_put(msg, sizeof(*itail));
372 osmo_store16be(iui->config.supported_versions_mask, &itail->versions_supported);
373 itail->spare = 0;
374 itail->data_pdu_type = iui->config.data_pdu_type;
375
376 payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
377 hdr->payload_crc_hi = (payload_crc >> 8) & 0x03;
378 hdr->payload_crc_lo = payload_crc & 0xff;
379
380
381 return itp;
382}
383
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100384static struct osmo_iuup_rnl_prim *irp_init_ind_alloc(struct osmo_iuup_instance *iui)
385{
386 struct osmo_iuup_rnl_prim *irp;
387
388 irp = osmo_iuup_rnl_prim_alloc(iui, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
389 irp->u.status.procedure = IUUP_PROC_INIT;
390 irp->u.status.u.initialization.mode_version = iui->mode_version;
391 irp->u.status.u.initialization.data_pdu_type = iui->config.data_pdu_type;
392 irp->u.status.u.initialization.num_subflows = iui->config.num_subflows;
393 irp->u.status.u.initialization.num_rfci = iui->config.num_rfci;
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100394 irp->u.status.u.initialization.IPTIs_present = iui->config.IPTIs_present;
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200395 memcpy(irp->u.status.u.initialization.rfci, iui->config.rfci, sizeof(iui->config.rfci));
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100396 return irp;
397}
398
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100399/* transform a RNL data primitive into a TNL data primitive (down the stack) */
400static struct osmo_iuup_tnl_prim *rnl_to_tnl_data(struct osmo_iuup_instance *iui,
401 struct osmo_iuup_rnl_prim *irp)
402{
403 struct osmo_iuup_tnl_prim *itp;
404 struct osmo_iuup_rnl_data dt;
405 struct msgb *msg;
406 uint16_t payload_crc;
407 struct iuup_pdutype0_hdr *h0;
408 struct iuup_pdutype1_hdr *h1;
409
410 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST));
411
412 msg = irp->oph.msg;
413 dt = irp->u.data;
414
415 /* pull up to the IuUP payload and push a new primitive header in front */
416 msgb_pull_to_l3(msg);
417
418 /* push the PDU TYPE 0 / 1 header in front of the payload */
419 switch (iui->config.data_pdu_type) {
420 case 0:
421 msg->l2h = msgb_push(msg, sizeof(*h0));
422 h0 = (struct iuup_pdutype0_hdr *)msg->l2h;
423 h0->frame_nr = dt.frame_nr;
424 h0->pdu_type = IUUP_PDU_T_DATA_CRC;
425 h0->rfci = dt.rfci;
426 h0->fqc = dt.fqc;
427 h0->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
428 payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
429 h0->payload_crc_hi = (payload_crc >> 8) & 0x03;
430 h0->payload_crc_lo = payload_crc & 0xff;
431 break;
432 case 1:
433 msg->l2h = msgb_push(msg, sizeof(*h1));
434 h1 = (struct iuup_pdutype1_hdr *)msg->l2h;
435 h1->frame_nr = dt.frame_nr;
436 h1->pdu_type = IUUP_PDU_T_DATA_NOCRC;
437 h1->rfci = dt.rfci;
438 h1->fqc = dt.fqc;
439 h1->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
440 h1->spare = 0;
441 break;
442 default:
443 OSMO_ASSERT(0);
444 }
445
446 /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
447 itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
448 osmo_prim_init(&itp->oph, SAP_IUUP_TNL, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, msg);
449
450 return itp;
451}
452
453/* transform a TNL primitive into a RNL primitive (up the stack) */
454static struct osmo_iuup_rnl_prim *tnl_to_rnl_data(struct osmo_iuup_tnl_prim *itp)
455{
456 struct msgb *msg;
457 struct iuup_pdutype0_hdr *h0;
458 struct iuup_pdutype1_hdr *h1;
459 struct osmo_iuup_rnl_data dt;
460 struct osmo_iuup_rnl_prim *irp;
461
462 msg = itp->oph.msg;
463
464 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION));
465
466 switch (iuup_get_pdu_type(msgb_l2(msg))) {
467 case IUUP_PDU_T_DATA_CRC:
468 h0 = (struct iuup_pdutype0_hdr *) msgb_l2(msg);
469 dt.rfci = h0->rfci;
470 dt.frame_nr = h0->frame_nr;
471 dt.fqc = h0->fqc;
472 break;
473 case IUUP_PDU_T_DATA_NOCRC:
474 h1 = (struct iuup_pdutype1_hdr *) msgb_l2(msg);
475 dt.rfci = h1->rfci;
476 dt.frame_nr = h1->frame_nr;
477 dt.fqc = h1->fqc;
478 break;
Pau Espin Pedrol09e54092022-06-29 18:17:56 +0200479 default:
480 OSMO_ASSERT(0);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100481 }
482
483 /* pull up to the IuUP payload and push a new primitive header in front */
484 msgb_pull_to_l3(msg);
485
486 /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
487 irp = (struct osmo_iuup_rnl_prim *) aligned_msgb_push(msg, sizeof(*irp));
488 osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION, msg);
489 irp->u.data = dt;
490
491 return irp;
492}
493
494static struct osmo_iuup_rnl_prim *irp_error_event_alloc_c(void *ctx, enum iuup_error_cause cause, enum iuup_error_distance distance)
495{
496 struct osmo_iuup_rnl_prim *irp;
497 struct msgb *msg;
498 msg = msgb_alloc_c(ctx, sizeof(*irp), "iuup-tx");
499 irp = (struct osmo_iuup_rnl_prim *) msgb_put(msg, sizeof(*irp));
500 osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, msg);
501 irp->u.status.procedure = IUUP_PROC_ERR_EVENT;
502 irp->u.status.u.error_event.cause = cause;
503 irp->u.status.u.error_event.distance = distance;
504 return irp;
505}
506
507static struct osmo_iuup_tnl_prim *itp_copy_c(void *ctx, const struct osmo_iuup_tnl_prim *src_itp)
508{
509 struct msgb *msg;
510 struct osmo_iuup_tnl_prim *dst_itp;
511
512 msg = msgb_copy_c(ctx, src_itp->oph.msg, "iuup-tx-retrans");
513 dst_itp = (struct osmo_iuup_tnl_prim *)msgb_data(msg);
514 dst_itp->oph.msg = msg;
515 return dst_itp;
516}
517
518static void retransmit_initialization(struct osmo_iuup_instance *iui)
519{
520 struct osmo_iuup_tnl_prim *itp;
521 iui->fi->T = IUUP_TIMER_INIT;
522 osmo_timer_schedule(&iui->fi->timer, iui->config.t_init.t_ms / 1000, (iui->config.t_init.t_ms % 1000) * 1000);
523 itp = itp_copy_c(iui, iui->timer.init.retrans_itp);
524 iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
525}
526
527/* return: whether the last Init was Acked correctly and hence can transition to next state */
528static bool iuup_rx_initialization(struct osmo_iuup_instance *iui, struct osmo_iuup_tnl_prim *itp)
529{
530 struct iuup_pdutype14_hdr *hdr;
531 struct iuup_ctrl_init_hdr *ihdr;
532 struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
533 struct iuup_ctrl_init_tail *itail;
534 enum iuup_error_cause err_cause;
535 uint8_t num_rfci = 0;
Harald Welte29814a52021-12-24 11:35:57 +0100536 int i;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100537 bool is_last;
538 uint16_t remote_mask, match_mask;
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100539 struct osmo_iuup_rnl_prim *irp;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100540 struct osmo_iuup_tnl_prim *resp;
541
542 /* TODO: whenever we check message boundaries, length, etc. and we fail, send NACK */
543
544 hdr = (struct iuup_pdutype14_hdr *)msgb_l2(itp->oph.msg);
545 ihdr = (struct iuup_ctrl_init_hdr *)hdr->payload;
546 if (ihdr->num_subflows_per_rfci == 0) {
547 LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected num_subflows=0 received\n");
548 err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
549 goto send_nack;
550 }
551 ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)ihdr->rfci_data;
552
553 do {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200554 struct osmo_iuup_rfci *rfci = &iui->config.rfci[num_rfci];
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100555 uint8_t l_size_bytes = ihdr_rfci->li + 1;
556 is_last = ihdr_rfci->lri;
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200557 if (num_rfci >= IUUP_MAX_RFCIS) {
558 LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Too many RFCIs received (%u)\n",
559 num_rfci);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100560 err_cause = IUUP_ERR_CAUSE_UNEXPECTED_RFCI;
561 goto send_nack;
562 }
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200563 rfci->used = 1;
564 rfci->id = ihdr_rfci->rfci;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100565 if (l_size_bytes == 2) {
566 uint16_t *subflow_size = (uint16_t *)ihdr_rfci->subflow_length;
567 for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200568 rfci->subflow_sizes[i] = osmo_load16be(subflow_size);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100569 subflow_size++;
570 }
571 } else {
572 uint8_t *subflow_size = ihdr_rfci->subflow_length;
573 for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200574 rfci->subflow_sizes[i] = *subflow_size;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100575 subflow_size++;
576 }
577 }
578 num_rfci++;
579 ihdr_rfci++;
580 ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)(((uint8_t *)ihdr_rfci) + ihdr->num_subflows_per_rfci * l_size_bytes);
581 } while (!is_last);
582
583 if (ihdr->ti) { /* Timing information present */
584 uint8_t *buf = (uint8_t *)ihdr_rfci;
585 uint8_t num_bytes = (num_rfci + 1) / 2;
586 iui->config.IPTIs_present = true;
587 for (i = 0; i < num_bytes - 1; i++) {
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200588 iui->config.rfci[i*2].IPTI = *buf >> 4;
589 iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100590 buf++;
591 }
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200592 iui->config.rfci[i*2].IPTI = *buf >> 4;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100593 if (!(num_rfci & 0x01)) /* is even: */
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +0200594 iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100595 buf++;
596 itail = (struct iuup_ctrl_init_tail *)buf;
597 } else {
Pau Espin Pedrol6d6d22e2022-05-27 14:30:17 +0200598 iui->config.IPTIs_present = false;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100599 itail = (struct iuup_ctrl_init_tail *)ihdr_rfci;
600 }
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100601 if (itail->data_pdu_type > 1) {
602 LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected Data PDU Type %u received\n", itail->data_pdu_type);
603 err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
604 goto send_nack;
605 }
606
607 remote_mask = osmo_load16be(&itail->versions_supported);
608 match_mask = (remote_mask & iui->config.supported_versions_mask);
609 if (match_mask == 0x0000) {
610 LOGPFSML(iui->fi, LOGL_NOTICE,
611 "Initialization: No match in supported versions local=0x%04x vs remote=0x%04x\n",
612 iui->config.supported_versions_mask, remote_mask);
613 err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
614 goto send_nack;
615 }
616 for (i = 15; i >= 0; i--) {
617 if (match_mask & (1<<i)) {
618 iui->mode_version = i;
619 break;
620 }
621 }
622
623 iui->config.num_rfci = num_rfci;
624 iui->config.num_subflows = ihdr->num_subflows_per_rfci;
625 iui->config.data_pdu_type = itail->data_pdu_type;
626
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100627 irp = irp_init_ind_alloc(iui);
628 iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
629
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100630 LOGPFSML(iui->fi, LOGL_DEBUG, "Tx Initialization ACK\n");
631 resp = itp_ctrl_ack_alloc(iui, IUUP_PROC_INIT, hdr->frame_nr);
632 iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
633 return ihdr->chain_ind == 0;
634send_nack:
635 LOGPFSML(iui->fi, LOGL_NOTICE, "Tx Initialization NACK cause=%u orig_message=%s\n",
636 err_cause, osmo_hexdump((const unsigned char *) msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg)));
637 resp = tnp_ctrl_nack_alloc(iui, IUUP_PROC_INIT, err_cause, hdr->frame_nr);
638 iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
639 return false;
640}
641
642/**********************
643 * FSM STATE FUNCTIONS
644 **********************/
645static void iuup_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
646{
647 struct osmo_iuup_instance *iui = fi->priv;
648 struct osmo_iuup_rnl_prim *user_prim = NULL;
649
650 switch (event) {
651 case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
652 user_prim = data;
653 iui->config = user_prim->u.config;
654 iui->config.supported_versions_mask &= 0x0003; /* We only support versions 1 and 2 ourselves */
655 //TODO: if supported_versions_mask == 0x0000,no supported versions, send error to upper layers
656
657 if (iui->config.transparent)
658 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_TrM_DATA_XFER_READY, 0, 0);
659 else {
660 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
661 }
662 break;
663 }
664}
665
666/* transparent mode data transfer */
667static void iuup_fsm_trm_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
668{
669 //struct osmo_iuup_instance *iui = fi->priv;
670
671 switch (event) {
672 case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
673 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
674 break;
675 case IUUP_FSM_EVT_IUUP_DATA_REQ:
676 /* Data coming down from RNL (user) towards TNL (transport) */
677 break;
678 case IUUP_FSM_EVT_IUUP_DATA_IND:
679 /* Data coming up from TNL (transport) towards RNL (user) */
680 break;
681 case IUUP_FSM_EVT_IUUP_UNITDATA_REQ:
682 case IUUP_FSM_EVT_IUUP_UNITDATA_IND:
683 case IUUP_FSM_EVT_SSASAR_UNITDATA_REQ:
684 case IUUP_FSM_EVT_SSASAR_UNITDATA_IND:
685 /* no state change */
686 break;
687 }
688}
689
690static void iuup_fsm_init_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
691{
692 struct osmo_iuup_instance *iui = fi->priv;
693
694 iui->type14_fn = 0;
695 if (iui->config.active) {
696 iui->timer.init.n = 0;
697 iui->timer.init.retrans_itp = tnp_ctrl_init_alloc(iui);
698 retransmit_initialization(iui);
699 }
700}
701
702static void iuup_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
703{
704 struct osmo_iuup_instance *iui = fi->priv;
705 struct osmo_iuup_rnl_prim *irp;
706 struct osmo_iuup_tnl_prim *itp;
707
708 switch (event) {
709 case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
710 /* the only permitted 'config req' type is the request to release the instance */
711 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
712 break;
713 case IUUP_FSM_EVT_INIT:
714 itp = data;
715 if (iuup_rx_initialization(iui, itp))
716 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
717 break;
718 case IUUP_FSM_EVT_LAST_INIT_ACK:
719 /* last INIT ACK was received, transition to DATA_XFER_READY state */
720 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
721 break;
722 case IUUP_FSM_EVT_INIT_NACK:
723 LOGPFSML(fi, LOGL_NOTICE, "Rx Initialization NACK N=%" PRIu32 "/%" PRIu32 "\n",
724 iui->timer.init.n, iui->config.t_init.n_max);
725 osmo_timer_del(&fi->timer);
726 if (iui->timer.init.n == iui->config.t_init.n_max) {
727 irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK, IUUP_ERR_DIST_SECOND_FWD);
728 iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
729 return;
730 }
731 iui->timer.init.n++;
732 retransmit_initialization(iui);
733 break;
734 default:
735 OSMO_ASSERT(false);
736 }
737}
738
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200739/* 3GPP TS 25.415 B.2.3 "Support Mode Data Transfer Ready State" */
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100740static void iuup_fsm_smpsdu_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
741{
742 struct osmo_iuup_instance *iui = fi->priv;
743 struct osmo_iuup_rnl_prim *irp = NULL;
744 struct osmo_iuup_tnl_prim *itp = NULL;
745
746 switch (event) {
747 case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
748 irp = data;
749 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
750 break;
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200751 case IUUP_FSM_EVT_INIT:
752 /* "In case of handover or relocation, Initialisation procedures
753 * may have to be performed and Iu UP instance may have to enter
754 * the initialisation state." */
755 itp = data;
756 if (!iuup_rx_initialization(iui, itp))
757 osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
758 break;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100759 case IUUP_FSM_EVT_IUUP_DATA_REQ:
760 /* Data coming down from RNL (user) towards TNL (transport) */
761 irp = data;
762 itp = rnl_to_tnl_data(iui, irp);
763 iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
764 break;
765 case IUUP_FSM_EVT_IUUP_DATA_IND:
766 /* Data coming up from TNL (transport) towards RNL (user) */
767 itp = data;
768 irp = tnl_to_rnl_data(itp);
769 iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
770 break;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100771 }
772}
773
774static int iuup_fsm_timer_cb(struct osmo_fsm_inst *fi)
775{
776 struct osmo_iuup_instance *iui = fi->priv;
777 struct osmo_iuup_rnl_prim *irp;
778
779 switch (fi->T) {
780 case IUUP_TIMER_INIT:
781 OSMO_ASSERT(fi->state == IUUP_FSM_ST_INIT);
782 if (iui->timer.init.n == iui->config.t_init.n_max) {
783 irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR, IUUP_ERR_DIST_LOCAL);
784 iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
785 return 0;
786 }
787 iui->timer.init.n++;
788 retransmit_initialization(iui);
789 break;
790 case IUUP_TIMER_TA:
791 break;
792 case IUUP_TIMER_RC:
793 break;
794 default:
795 OSMO_ASSERT(0);
796 }
797 return 0;
798}
799
800
801static const struct osmo_fsm_state iuup_fsm_states[] = {
802 [IUUP_FSM_ST_NULL] = {
803 .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ),
804 .out_state_mask = S(IUUP_FSM_ST_INIT) |
805 S(IUUP_FSM_ST_TrM_DATA_XFER_READY),
806 .name = "NULL",
807 .action = iuup_fsm_null,
808 },
809 [IUUP_FSM_ST_TrM_DATA_XFER_READY] = {
810 .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
811 S(IUUP_FSM_EVT_IUUP_STATUS_REQ) |
812 S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
813 S(IUUP_FSM_EVT_IUUP_DATA_IND) |
814 S(IUUP_FSM_EVT_IUUP_UNITDATA_REQ) |
815 S(IUUP_FSM_EVT_IUUP_UNITDATA_IND) |
816 S(IUUP_FSM_EVT_SSASAR_UNITDATA_REQ) |
817 S(IUUP_FSM_EVT_SSASAR_UNITDATA_IND),
818 .out_state_mask = S(IUUP_FSM_ST_NULL),
Philipp Maierbdd7df32022-01-18 11:58:18 +0100819 .name = "TrM_Data_Transfer_Ready",
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100820 .action = iuup_fsm_trm_data,
821 },
822 [IUUP_FSM_ST_INIT] = {
823 .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
824 S(IUUP_FSM_EVT_INIT) |
825 S(IUUP_FSM_EVT_LAST_INIT_ACK) |
826 S(IUUP_FSM_EVT_INIT_NACK),
827 .out_state_mask = S(IUUP_FSM_ST_NULL) |
828 S(IUUP_FSM_ST_SMpSDU_DATA_XFER_READY),
829 .name = "Initialisation",
830 .onenter = iuup_fsm_init_on_enter,
831 .action = iuup_fsm_init,
832 },
833 [IUUP_FSM_ST_SMpSDU_DATA_XFER_READY] = {
Pau Espin Pedrol0664a3e2022-06-13 13:12:24 +0200834 .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200835 S(IUUP_FSM_EVT_INIT) |
Pau Espin Pedrol0664a3e2022-06-13 13:12:24 +0200836 S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100837 S(IUUP_FSM_EVT_IUUP_DATA_IND),
838 .out_state_mask = S(IUUP_FSM_ST_NULL) |
839 S(IUUP_FSM_ST_INIT),
Philipp Maierbdd7df32022-01-18 11:58:18 +0100840 .name = "SMpSDU_Data_Transfer_Ready",
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100841 .action = iuup_fsm_smpsdu_data,
842 },
843};
844
845static struct osmo_fsm iuup_fsm = {
846 .name = "IuUP",
847 .states = iuup_fsm_states,
848 .num_states = ARRAY_SIZE(iuup_fsm_states),
849 .timer_cb = iuup_fsm_timer_cb,
850 .log_subsys = DLIUUP,
851 .event_names = iuup_fsm_event_names,
852};
853
854static int iuup_verify_pdu(const uint8_t *data, unsigned int len)
855{
856 int header_crc_computed, payload_crc_computed;
857 uint16_t payload_crc;
858 uint8_t pdu_type = iuup_get_pdu_type(data);
859 struct iuup_pdutype0_hdr *t0h;
860 struct iuup_pdutype14_hdr *t14h;
861
862 if (len < 3)
863 return -EINVAL;
864
865 header_crc_computed = osmo_iuup_compute_header_crc(data, len);
866 if (iuup_get_hdr_crc(data) != header_crc_computed) {
Pau Espin Pedrol13227552022-01-10 14:21:19 +0100867 LOGP(DLIUUP, LOGL_NOTICE, "Header Checksum error: rx 0x%02x vs exp 0x%02x\n",
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100868 iuup_get_hdr_crc(data), header_crc_computed);
869 return -EIO;
870 }
871 switch (pdu_type) {
872 case IUUP_PDU_T_DATA_NOCRC:
873 if (len < 4)
874 return -EINVAL;
875 break;
876 case IUUP_PDU_T_DATA_CRC:
877 t0h = (struct iuup_pdutype0_hdr *) data;
878 payload_crc = ((uint16_t)t0h->payload_crc_hi << 8) | t0h->payload_crc_lo;
879 payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
880 if (payload_crc != payload_crc_computed)
Pau Espin Pedrol13227552022-01-10 14:21:19 +0100881 goto payload_crc_err;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100882 break;
883 case IUUP_PDU_T_CONTROL:
884 t14h = (struct iuup_pdutype14_hdr *) data;
885 if (t14h->ack_nack == IUUP_AN_PROCEDURE) {
886 payload_crc = ((uint16_t)t14h->payload_crc_hi << 8) | t14h->payload_crc_lo;
887 payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
888 if (payload_crc != payload_crc_computed)
Pau Espin Pedrol13227552022-01-10 14:21:19 +0100889 goto payload_crc_err;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100890 }
891 break;
892 default:
893 return -EINVAL;
894 }
895 return 0;
Pau Espin Pedrol13227552022-01-10 14:21:19 +0100896
897payload_crc_err:
898 LOGP(DLIUUP, LOGL_NOTICE, "Payload Checksum error (pdu type %u): rx 0x%02x vs exp 0x%02x\n",
899 pdu_type, payload_crc, payload_crc_computed);
900 return -EIO;
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100901}
902
903/* A IuUP TNL SAP primitive from transport (lower layer) */
904int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *inst, struct osmo_iuup_tnl_prim *itp)
905{
906 struct osmo_prim_hdr *oph = &itp->oph;
907 struct iuup_pdutype14_hdr *t14h;
908 int rc = 0;
909
910 OSMO_ASSERT(oph->sap == SAP_IUUP_TNL);
911
912 switch (OSMO_PRIM_HDR(oph)) {
913 case OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION):
914 if (iuup_verify_pdu(msgb_l2(oph->msg), msgb_l2len(oph->msg)) < 0) {
915 LOGPFSML(inst->fi, LOGL_NOTICE, "Discarding invalid IuUP PDU: %s\n",
916 osmo_hexdump((const unsigned char *) msgb_l2(oph->msg), msgb_l2len(oph->msg)));
917 /* don't return error as the caller is not responsible for the PDU which
918 * was transmitted from some remote peer */
919 return 0;
920 }
921 switch (iuup_get_pdu_type(msgb_l2(oph->msg))) {
922 case IUUP_PDU_T_DATA_CRC:
923 oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype0_hdr);
924 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
925 break;
926 case IUUP_PDU_T_DATA_NOCRC:
927 oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype1_hdr);
928 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
929 break;
930 case IUUP_PDU_T_CONTROL:
931 t14h = (struct iuup_pdutype14_hdr *) msgb_l2(oph->msg);
932 switch (t14h->ack_nack) {
933 case IUUP_AN_PROCEDURE:
934 switch (t14h->proc_ind) {
935 case IUUP_PROC_INIT:
936 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_INIT, itp);
937 break;
938 case IUUP_PROC_RATE_CTRL:
939 case IUUP_PROC_TIME_ALIGN:
940 case IUUP_PROC_ERR_EVENT:
941 LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
942 "unsupported IuUP procedure %u\n", t14h->proc_ind);
943 break;
944 default:
945 LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
946 "unknown IuUP procedure %u\n", t14h->proc_ind);
947 break;
948 }
949 break;
950 case IUUP_AN_ACK:
951 switch (t14h->proc_ind) {
952 case IUUP_PROC_INIT:
953 rc = osmo_fsm_inst_dispatch(inst->fi,
954 IUUP_FSM_EVT_LAST_INIT_ACK, itp);
955 break;
956 default:
957 LOGPFSML(inst->fi, LOGL_ERROR, "Received ACK for "
958 "unknown IuUP procedure %u\n", t14h->proc_ind);
959 break;
960 }
961 break;
962 case IUUP_AN_NACK:
963 switch (t14h->proc_ind) {
964 case IUUP_PROC_INIT:
965 rc = osmo_fsm_inst_dispatch(inst->fi,
966 IUUP_FSM_EVT_INIT_NACK, itp);
967 break;
968 default:
969 LOGPFSML(inst->fi, LOGL_ERROR, "Received NACK for "
970 "unknown IuUP procedure %u\n", t14h->proc_ind);
971 break;
972 }
973 break;
974 default:
975 LOGPFSML(inst->fi, LOGL_ERROR, "Received unknown IuUP ACK/NACK\n");
976 break;
977 }
978 break;
979 default:
980 LOGPFSML(inst->fi, LOGL_NOTICE, "Received unknown IuUP PDU type %u\n",
981 iuup_get_pdu_type(msgb_l2(oph->msg)));
982 break;
983 }
984 break;
985 default:
986 /* exception: return an error code due to a wrong primitive */
987 return -EINVAL;
988 }
989
990 return rc;
991}
992
993/* A IuUP RNL SAP primitive from user (higher layer) */
994int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_iuup_rnl_prim *irp)
995{
996 struct osmo_prim_hdr *oph = &irp->oph;
997 int rc;
998
999 OSMO_ASSERT(oph->sap == SAP_IUUP_RNL);
1000
1001 switch (OSMO_PRIM_HDR(oph)) {
1002 case OSMO_PRIM(OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST):
1003 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_CONFIG_REQ, irp);
1004 msgb_free(irp->oph.msg);
1005 break;
1006 case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST):
1007 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_REQ, irp);
1008 if (rc != 0)
1009 msgb_free(irp->oph.msg);
1010 break;
1011 case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_REQUEST):
1012 rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_STATUS_REQ, irp);
1013 msgb_free(irp->oph.msg);
1014 break;
1015 default:
1016 rc = -EINVAL;
1017 msgb_free(irp->oph.msg);
1018 }
1019 return rc;
1020}
1021
1022struct osmo_iuup_instance *osmo_iuup_instance_alloc(void *ctx, const char *id)
1023{
1024 struct osmo_iuup_instance *iui;
1025 iui = talloc_zero(ctx, struct osmo_iuup_instance);
1026 if (!iui)
1027 return NULL;
1028
1029 iui->fi = osmo_fsm_inst_alloc(&iuup_fsm, NULL, iui, LOGL_DEBUG, id);
1030 if (!iui->fi)
1031 goto free_ret;
1032
1033 return iui;
1034free_ret:
1035 talloc_free(iui);
1036 return NULL;
1037}
1038
1039void osmo_iuup_instance_free(struct osmo_iuup_instance *iui)
1040{
1041 if (!iui)
1042 return;
1043
1044 if (iui->fi)
1045 osmo_fsm_inst_free(iui->fi);
1046 iui->fi = NULL;
1047 talloc_free(iui);
1048}
1049
1050void osmo_iuup_instance_set_user_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
1051{
1052 iui->user_prim_cb = func;
1053 iui->user_prim_priv = priv;
1054}
1055void osmo_iuup_instance_set_transport_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
1056{
1057 iui->transport_prim_cb = func;
1058 iui->transport_prim_priv = priv;
1059}
1060
1061static __attribute__((constructor)) void on_dso_load_iuup_fsm(void)
1062{
1063 OSMO_ASSERT(osmo_fsm_register(&iuup_fsm) == 0);
1064}