blob: f3aba665621ae89e17e71ae7d54b4d07b97d87ba [file] [log] [blame]
Harald Welte9fe1f9f2018-11-29 13:47:39 +01001#include <stdint.h>
2#include <stdio.h>
3
4#include <osmocom/core/application.h>
5#include <osmocom/core/logging.h>
6#include <osmocom/core/utils.h>
7#include <osmocom/core/msgb.h>
8#include <osmocom/core/fsm.h>
9#include <osmocom/core/select.h>
10
11#include <osmocom/gsm/prim.h>
12#include <osmocom/gsm/iuup.h>
13
14static void *iuup_test_ctx;
15
16static struct osmo_iuup_rnl_config def_configure_req = {
17 .transparent = false,
18 .active = true,
19 .supported_versions_mask = 0x0001,
20 .num_rfci = 3,
21 .num_subflows = 3,
Pau Espin Pedrol510f4c92022-05-24 18:44:10 +020022 .IPTIs_present = true,
23 .rfci = {
24 {.used = 1, .id = 0, .IPTI = 1, .subflow_sizes = {81, 103, 60} },
25 {.used = 1, .id = 1, .IPTI = 7, .subflow_sizes = {39, 0, 0} },
26 {.used = 1, .id = 2, .IPTI = 1, .subflow_sizes = {0, 0, 0} },
Harald Welte9fe1f9f2018-11-29 13:47:39 +010027 },
28 /* .delivery_err_sdu = All set to 0 (YES) by default, */
29 .IPTIs_present = true,
Harald Welte9fe1f9f2018-11-29 13:47:39 +010030 .t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
31 .t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
32 .t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
33};
34
35/* Frame 33, "Initialization", OS#4744 3g_call_23112021.pcapng
36IuUP
37 1110 .... = PDU Type: Control Procedure (14)
38 .... 00.. = Ack/Nack: Procedure (0)
39 .... ..00 = Frame Number: 0
40 0000 .... = Mode Version: 0x0
41 .... 0000 = Procedure: Initialization (0)
42 1101 11.. = Header CRC: 0x37 [correct]
43 .... ..11 1001 1001 = Payload CRC: 0x399
44 000. .... = Spare: 0x0
45 ...1 .... = TI: IPTIs present in frame (1)
46 .... 011. = Subflows: 3
47 .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
48 RFCI 0 Initialization
49 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
50 .0.. .... = RFCI 0 LI: one octet used (0x0)
51 ..00 0000 = RFCI 0: 0
52 RFCI 0 Flow 0 Len: 81
53 RFCI 0 Flow 1 Len: 103
54 RFCI 0 Flow 2 Len: 60
55 RFCI 1 Initialization
56 0... .... = RFCI 1 LRI: Not last RFCI (0x0)
57 .0.. .... = RFCI 1 LI: one octet used (0x0)
58 ..00 0001 = RFCI 1: 1
59 RFCI 1 Flow 0 Len: 39
60 RFCI 1 Flow 1 Len: 0
61 RFCI 1 Flow 2 Len: 0
62 RFCI 2 Initialization
63 1... .... = RFCI 2 LRI: Last RFCI in current frame (0x1)
64 .0.. .... = RFCI 2 LI: one octet used (0x0)
65 ..00 0010 = RFCI 2: 2
66 RFCI 2 Flow 0 Len: 0
67 RFCI 2 Flow 1 Len: 0
68 RFCI 2 Flow 2 Len: 0
69 IPTIs
70 0001 .... = RFCI 0 IPTI: 0x1
71 .... 0111 = RFCI 1 IPTI: 0x7
72 0001 .... = RFCI 2 IPTI: 0x1
73 Iu UP Mode Versions Supported: 0x0001
74 0... .... .... .... = Version 16: not supported (0x0)
75 .0.. .... .... .... = Version 15: not supported (0x0)
76 ..0. .... .... .... = Version 14: not supported (0x0)
77 ...0 .... .... .... = Version 13: not supported (0x0)
78 .... 0... .... .... = Version 12: not supported (0x0)
79 .... .0.. .... .... = Version 11: not supported (0x0)
80 .... ..0. .... .... = Version 10: not supported (0x0)
81 .... ...0 .... .... = Version 9: not supported (0x0)
82 .... .... 0... .... = Version 8: not supported (0x0)
83 .... .... .0.. .... = Version 7: not supported (0x0)
84 .... .... ..0. .... = Version 6: not supported (0x0)
85 .... .... ...0 .... = Version 5: not supported (0x0)
86 .... .... .... 0... = Version 4: not supported (0x0)
87 .... .... .... .0.. = Version 3: not supported (0x0)
88 .... .... .... ..0. = Version 2: not supported (0x0)
89 .... .... .... ...1 = Version 1: supported (0x1)
90 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
91*/
92static const uint8_t iuup_initialization[] = {
93 0xe0, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
94 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
95};
96
97/* Frame 87, "Data RFCI=0 FN = 1", OS#4744 3g_call_23112021.pcapng
98IuUP
99 0000 .... = PDU Type: Data with CRC (0)
100 .... 0001 = Frame Number: 1
101 00.. .... = FQC: Frame Good (0)
102 ..00 0000 = RFCI: 0x00
103 1110 00.. = Header CRC: 0x38 [correct]
104 .... ..11 1111 1111 = Payload CRC: 0x3ff
105 Payload Data: 08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740
106*/
107static const uint8_t iuup_data[] = {
108 0x01, 0x00, 0xe3, 0xff, /*payload starts here: */ 0x08, 0x55, 0x6d, 0x94, 0x4c, 0x71, 0xa1, 0xa0,
109 0x81, 0xe7, 0xea, 0xd2, 0x04, 0x24, 0x44, 0x80, 0x00, 0x0e, 0xcd, 0x82,
110 0xb8, 0x11, 0x18, 0x00, 0x00, 0x97, 0xc4, 0x79, 0x4e, 0x77, 0x40
111};
112
113#define IUUP_MSGB_SIZE 4096
114
115static struct osmo_iuup_tnl_prim *itp_ctrl_nack_alloc(enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
116{
117 struct osmo_iuup_tnl_prim *tnp;
118 struct iuup_ctrl_nack *nack;
119 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
120 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_nack));
121 nack = (struct iuup_ctrl_nack *) msgb_l2(tnp->oph.msg);
122 *nack = (struct iuup_ctrl_nack){
123 .hdr = {
124 .frame_nr = fn,
125 .ack_nack = IUUP_AN_NACK,
126 .pdu_type = IUUP_PDU_T_CONTROL,
127 .proc_ind = proc_ind,
128 .mode_version = 0,
129 .payload_crc_hi = 0,
130 .header_crc = 0,
131 .payload_crc_lo = 0,
132 },
133 .spare = 0,
134 .error_cause = error_cause,
135 };
136 nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
137 return tnp;
138}
139
140static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(enum iuup_procedure proc_ind, uint8_t fn)
141{
142 struct osmo_iuup_tnl_prim *tnp;
143 struct iuup_ctrl_ack *ack;
144 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
145 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_ack));
146 ack = (struct iuup_ctrl_ack *) msgb_l2(tnp->oph.msg);
147 *ack = (struct iuup_ctrl_ack){
148 .hdr = {
149 .frame_nr = fn,
150 .ack_nack = IUUP_AN_ACK,
151 .pdu_type = IUUP_PDU_T_CONTROL,
152 .proc_ind = proc_ind,
153 .mode_version = 0,
154 .payload_crc_hi = 0,
155 .header_crc = 0,
156 .payload_crc_lo = 0,
157 },
158 };
159 ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
160 return tnp;
161}
162
163static void clock_override_set(long sec, long usec)
164{
165 osmo_gettimeofday_override_time.tv_sec = sec + usec / (1000*1000);
166 osmo_gettimeofday_override_time.tv_usec = usec % (1000*1000);
167 printf("sys={%lu.%06lu}, %s\n", osmo_gettimeofday_override_time.tv_sec,
168 osmo_gettimeofday_override_time.tv_usec, __func__);
169}
170
171void test_crc(void)
172{
173 int rc;
174
175 /* Frame 34, "Initialization ACK", OS#4744 3g_call_23112021.pcapng */
176 static const uint8_t iuup_initialization_ack[] = {
177 0xe4, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
178 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
179 };
180
181 printf("=== start: %s ===\n", __func__);
182
183 rc = osmo_iuup_compute_header_crc(iuup_initialization, sizeof(iuup_initialization));
184 printf("iuup_initialization: Header CRC = 0x%02x\n", rc);
185 rc = osmo_iuup_compute_payload_crc(iuup_initialization, sizeof(iuup_initialization));
186 printf("iuup_initialization: Payload CRC = 0x%03x\n", rc);
187
188 rc = osmo_iuup_compute_header_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
189 printf("iuup_initialization_ack: Header CRC = 0x%02x\n", rc);
190 rc = osmo_iuup_compute_payload_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
191 printf("iuup_initialization_ack: Payload CRC = 0x%03x\n", rc);
192
193 printf("=== end: %s ===\n", __func__);
194}
195
196
197/****************************
198 * test_tinit_timeout_retrans
199 ****************************/
200static unsigned int _tinit_timeout_retrans_user_rx_prim = 0;
201static int _tinit_timeout_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
202{
203 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
204 printf("%s()\n", __func__);
205
206 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
207
208 OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
209 OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR);
210 OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_LOCAL);
211 _tinit_timeout_retrans_user_rx_prim++;
212 msgb_free(oph->msg);
213 return 0;
214}
215static unsigned int _tinit_timeout_retrans_transport_rx_prim = 0;
216static int _tinit_timeout_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
217{
218 struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
219 struct msgb *msg = oph->msg;
220
221 printf("%s()\n", __func__);
222 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
223 printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
224 osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
225 _tinit_timeout_retrans_transport_rx_prim++;
226
227 msgb_free(msg);
228 return 0;
229}
230void test_tinit_timeout_retrans(void)
231{
232 struct osmo_iuup_instance *iui;
233 struct osmo_iuup_rnl_prim *rnp;
234 int rc, i;
235
236 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
237 OSMO_ASSERT(iui);
238 osmo_iuup_instance_set_user_prim_cb(iui, _tinit_timeout_retrans_user_prim_cb, NULL);
239 osmo_iuup_instance_set_transport_prim_cb(iui, _tinit_timeout_retrans_transport_prim_cb, NULL);
240
241 clock_override_set(0, 0);
242
243 /* Tx CONFIG.req */
244 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
245 rnp->u.config = def_configure_req;
246 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
247 /* STATUS-INIT.req is transmitted automatically: */
248 OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == 1);
249
250 /* After one sec, INITIALIZATION msg is retransmitted */
251 for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
252 clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
253 osmo_select_main(0);
254 OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i + 1);
255 }
256 /* Last one should send an error event: */
257 OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 0);
258 clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
259 osmo_select_main(0);
260 OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
261 OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);
262
263 /* Nothing else is received afterwards. osmo_select_main() will block forever. */
264 /*clock_override_set(i + 1, 0);
265 osmo_select_main(0);
266 OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
267 OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);*/
268
269 osmo_iuup_instance_free(iui);
270}
271
272/****************************
273 * test_tinit_nack
274 ****************************/
275static unsigned int _init_nack_retrans_user_rx_prim = 0;
276static int _init_nack_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
277{
278 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
279
280 printf("%s()\n", __func__);
281
282 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
283
284 OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
285 OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK);
286 OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_SECOND_FWD);
287 _init_nack_retrans_user_rx_prim++;
288 msgb_free(oph->msg);
289 return 0;
290}
291static int _init_nack_retrans_transport_rx_prim = 0;
292static int _init_nack_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
293{
294 struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
295 struct msgb *msg = oph->msg;
296
297 printf("%s()\n", __func__);
298 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
299 printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
300 osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
301 _init_nack_retrans_transport_rx_prim++;
302
303 msgb_free(msg);
304 return 0;
305}
306void test_init_nack_retrans(void)
307{
308 struct osmo_iuup_instance *iui;
309 struct osmo_iuup_rnl_prim *rnp;
310 struct osmo_iuup_tnl_prim *tnp;
311 int rc, i;
312
313 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
314 OSMO_ASSERT(iui);
315 osmo_iuup_instance_set_user_prim_cb(iui, _init_nack_retrans_user_prim_cb, NULL);
316 osmo_iuup_instance_set_transport_prim_cb(iui, _init_nack_retrans_transport_prim_cb, NULL);
317
318 clock_override_set(0, 0);
319
320 /* Tx CONFIG.req */
321 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
322 rnp->u.config = def_configure_req;
323 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
324 /* STATUS-INIT.req is transmitted automatically: */
325 OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == 1);
326
327 /* After one sec, INITIALIZATION msg is retransmitted */
328 for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
329 /* Send NACK: */
330 tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
331 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
332 /* A new INIT is retransmitted: */
333 OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i + 1);
334 }
335 /* Last one should send an error event: */
336 OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 0);
337 tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
338 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
339 OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i);
340 OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 1);
341
342 /* Nothing else is received afterwards. osmo_select_main() will block forever. */
343
344 osmo_iuup_instance_free(iui);
345}
346
347
348/****************************
349 * test_init_ack
350 ****************************/
351static unsigned int _init_ack_user_rx_prim = 0;
352static int _init_ack_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
353{
354 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
355 struct msgb *msg = oph->msg;
356
357 printf("%s()\n", __func__);
358
359 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
360 printf("User: UL len=%u: %s\n", msgb_l3len(msg),
361 osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
362
363 _init_ack_user_rx_prim++;
364 msgb_free(oph->msg);
365 return 0;
366}
367static int _init_ack_transport_rx_prim = 0;
368static int _init_ack_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
369{
370 struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
371 struct msgb *msg = oph->msg;
372
373 printf("%s()\n", __func__);
374 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
375 printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
376 osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
377 _init_ack_transport_rx_prim++;
378
379 msgb_free(msg);
380 return 0;
381}
382void test_init_ack(void)
383{
384 struct osmo_iuup_instance *iui;
385 struct osmo_iuup_rnl_prim *rnp;
386 struct osmo_iuup_tnl_prim *tnp;
387 struct iuup_pdutype0_hdr *hdr0;
388 int rc;
389
390 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
391 OSMO_ASSERT(iui);
392 osmo_iuup_instance_set_user_prim_cb(iui, _init_ack_user_prim_cb, NULL);
393 osmo_iuup_instance_set_transport_prim_cb(iui, _init_ack_transport_prim_cb, NULL);
394
395 clock_override_set(0, 0);
396
397 /* Tx CONFIG.req */
398 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
399 rnp->u.config = def_configure_req;
400 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
401 /* STATUS-INIT.req is transmitted automatically: */
402 OSMO_ASSERT(_init_ack_transport_rx_prim == 1);
403
404 /* Send ACK: */
405 tnp = itp_ctrl_ack_alloc(IUUP_PROC_INIT, 0);
406 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
407 OSMO_ASSERT(_init_ack_transport_rx_prim == 1); /* Make sure there's no retrans */
408 OSMO_ASSERT(_init_ack_user_rx_prim == 0); /* Make sure there's no error event */
409
410 /* Send IuUP incoming data to the implementation: */
411 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
412 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
413 hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
414 memcpy(hdr0, iuup_data, sizeof(iuup_data));
415 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
416 /* We receive it in RNL: */
417 OSMO_ASSERT(_init_ack_user_rx_prim == 1);
418
419 /* Now in opposite direction, RNL->[IuuP]->TNL: */
420 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
421 rnp->u.data.rfci = 0;
422 rnp->u.data.frame_nr = 1;
423 rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
424 rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
425 memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
426 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
427 OSMO_ASSERT(_init_ack_transport_rx_prim == 2); /* We receive data in TNL */
428
429 osmo_iuup_instance_free(iui);
430}
431
432/****************************
433 * test_passive_init
434 ****************************/
435static unsigned int _passive_init_user_rx_prim = 0;
436static int _passive_init_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
437{
438 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
439 struct msgb *msg = oph->msg;
440
441 printf("%s()\n", __func__);
442
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100443 switch (_passive_init_user_rx_prim) {
444 case 0:
445 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
446 OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
447 break;
448 case 1:
449 default:
450 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
451 printf("User: UL len=%u: %s\n", msgb_l3len(msg),
452 osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
453 }
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100454
455 _passive_init_user_rx_prim++;
456 msgb_free(oph->msg);
457 return 0;
458}
459static int _passive_init_transport_rx_prim = 0;
460static int _passive_init_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
461{
462 struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
463 struct msgb *msg;
464
465 printf("%s()\n", __func__);
466 msg = oph->msg;
467 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
468 printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
469 osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
470 _passive_init_transport_rx_prim++;
471
472 msgb_free(msg);
473 return 0;
474}
475void test_passive_init(void)
476{
477 /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
478 struct osmo_iuup_instance *iui;
479 struct osmo_iuup_rnl_prim *rnp;
480 struct osmo_iuup_tnl_prim *tnp;
481 struct iuup_pdutype14_hdr *hdr14;
482 struct iuup_pdutype0_hdr *hdr0;
483 int rc;
484
485 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
486 OSMO_ASSERT(iui);
487 osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_user_prim_cb, NULL);
488 osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
489
490 clock_override_set(0, 0);
491
492 /* Tx CONFIG.req */
493 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
494 rnp->u.config = def_configure_req;
495 rnp->u.config.active = false;
496 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
497 /* STATUS-INIT.req is NOT transmitted automatically: */
498 OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
499
500 /* Send Init: */
501 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
502 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
503 hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
504 memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
505 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
506 OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100507 OSMO_ASSERT(_passive_init_user_rx_prim == 1); /* We receive the Status-Init.ind */
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100508
509 /* Send IuUP incoming data to the implementation: */
510 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
511 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
512 hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
513 memcpy(hdr0, iuup_data, sizeof(iuup_data));
514 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
515 /* We receive it in RNL: */
Pau Espin Pedrol604eaba2022-01-03 16:57:45 +0100516 OSMO_ASSERT(_passive_init_user_rx_prim == 2);
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100517
518 /* Now in opposite direction, RNL->[IuuP]->TNL: */
519 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
520 rnp->u.data.rfci = 0;
521 rnp->u.data.frame_nr = 1;
522 rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
523 rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
524 memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
525 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
526 OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive data in TNL */
527
528 osmo_iuup_instance_free(iui);
529}
530
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200531/****************************
532 * test_passive_init_retrans
533 ****************************/
534static unsigned int _passive_init_retrans_user_rx_prim = 0;
535static int _passive_init_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
536{
537 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
538 struct msgb *msg = oph->msg;
539
540 printf("%s()\n", __func__);
541
542 switch (_passive_init_retrans_user_rx_prim) {
543 case 0:
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200544 case 1:
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200545 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
546 OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
547 break;
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200548 case 2:
549 default:
550 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
551 printf("User: UL len=%u: %s\n", msgb_l3len(msg),
552 osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
553 }
554
555 _passive_init_retrans_user_rx_prim++;
556 msgb_free(oph->msg);
557 return 0;
558}
559void test_passive_init_retrans(void)
560{
561 /* Here we check the passive INIT code path, aka receiving INIT and
562 * returning INIT_ACK/NACK. We emulate the peer not receiving the INIT
563 * ACK and hence retransmitting the INIT. The IuUP stack should then
564 * push the new INIT info up the stack and ACK it. */
565 struct osmo_iuup_instance *iui;
566 struct osmo_iuup_rnl_prim *rnp;
567 struct osmo_iuup_tnl_prim *tnp;
568 struct iuup_pdutype14_hdr *hdr14;
569 struct iuup_pdutype0_hdr *hdr0;
570 int rc;
571
572 /* reset global var, we reuse it together wth callback from test_passive_init(): */
573 _passive_init_transport_rx_prim = 0;
574
575 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
576 OSMO_ASSERT(iui);
577 osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_retrans_user_prim_cb, NULL);
578 osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
579
580 clock_override_set(0, 0);
581
582 /* Tx CONFIG.req */
583 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
584 rnp->u.config = def_configure_req;
585 rnp->u.config.active = false;
586 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
587 /* STATUS-INIT.req is NOT transmitted automatically: */
588 OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
589
590 /* Send Init: */
591 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
592 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
593 hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
594 memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
595 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
596 OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
597 OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 1); /* We receive the Status-Init.ind */
598
599 /* Send Init (retrans): */
600 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
601 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
602 hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
603 memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200604 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
605 OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive another Init ACK */
606 OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 2); /* We receive another Status-Init.ind */
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200607
608 /* Send IuUP incoming data to the implementation: */
609 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
610 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
611 hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
612 memcpy(hdr0, iuup_data, sizeof(iuup_data));
613 OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
614 /* We receive it in RNL: */
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200615 OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 3);
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200616
617 /* Now in opposite direction, RNL->[IuuP]->TNL: */
618 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
619 rnp->u.data.rfci = 0;
620 rnp->u.data.frame_nr = 1;
621 rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
622 rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
623 memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
624 OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
Pau Espin Pedrol3278f0f2022-06-13 13:00:53 +0200625 OSMO_ASSERT(_passive_init_transport_rx_prim == 3); /* We receive data in TNL */
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200626
627 osmo_iuup_instance_free(iui);
628}
629
Pau Espin Pedrol6d6d22e2022-05-27 14:30:17 +0200630static int _decode_passive_init_2_rfci_no_iptis_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
631{
632 struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
633 printf("%s(): Initialization decoded fine!\n", __func__);
634 OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
635 OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
636 OSMO_ASSERT(irp->u.status.u.initialization.num_rfci == 2);
637 OSMO_ASSERT(irp->u.status.u.initialization.num_subflows == 3);
638 OSMO_ASSERT(irp->u.status.u.initialization.data_pdu_type == 0);
639 OSMO_ASSERT(irp->u.status.u.initialization.IPTIs_present == false);
640 msgb_free(oph->msg);
641 return 0;
642}
643static int _decode_passive_init_2_rfci_no_iptis_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
644{
645 struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
646 struct msgb *msg;
647 struct iuup_pdutype14_hdr *hdr;
648
649 printf("%s()\n", __func__);
650 msg = oph->msg;
651 OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
652 printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
653 osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
654 hdr = msgb_l2(msg);
655 OSMO_ASSERT(hdr->pdu_type == IUUP_PDU_T_CONTROL);
656 OSMO_ASSERT(hdr->ack_nack == IUUP_AN_ACK);
657 msgb_free(msg);
658 return 0;
659}
660void test_decode_passive_init_2_rfci_no_iptis(void)
661{
662 /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
663 struct osmo_iuup_instance *iui;
664 struct osmo_iuup_rnl_prim *rnp;
665 struct osmo_iuup_tnl_prim *tnp;
666 struct iuup_pdutype14_hdr *hdr14;
667 int rc;
668
669 /* Frame 46, "Initialization", SYS#5969 call4_Iu_Iuh.pcap
670 1110 .... = PDU Type: Control Procedure (14)
671 .... 00.. = Ack/Nack: Procedure (0)
672 .... ..00 = Frame Number: 0
673 0000 .... = Mode Version: 0x0
674 .... 0000 = Procedure: Initialization (0)
675 1101 11.. = Header CRC: 0x37 [correct]
676 .... ..01 1011 0100 = Payload CRC: 0x1b4
677 000. .... = Spare: 0x0
678 ...0 .... = TI: IPTIs not present (0)
679 .... 011. = Subflows: 3
680 .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
681 RFCI 1 Initialization
682 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
683 .0.. .... = RFCI 0 LI: one octet used (0x0)
684 ..00 0001 = RFCI 0: 1
685 RFCI 0 Flow 0 Len: 81
686 RFCI 0 Flow 1 Len: 103
687 RFCI 0 Flow 2 Len: 60
688 RFCI 6 Initialization
689 1... .... = RFCI 1 LRI: Last RFCI in current frame (0x1)
690 .0.. .... = RFCI 1 LI: one octet used (0x0)
691 ..00 0110 = RFCI 1: 6
692 RFCI 1 Flow 0 Len: 39
693 RFCI 1 Flow 1 Len: 0
694 RFCI 1 Flow 2 Len: 0
695 Iu UP Mode Versions Supported: 0x0001
696 0... .... .... .... = Version 16: not supported (0x0)
697 .0.. .... .... .... = Version 15: not supported (0x0)
698 ..0. .... .... .... = Version 14: not supported (0x0)
699 ...0 .... .... .... = Version 13: not supported (0x0)
700 .... 0... .... .... = Version 12: not supported (0x0)
701 .... .0.. .... .... = Version 11: not supported (0x0)
702 .... ..0. .... .... = Version 10: not supported (0x0)
703 .... ...0 .... .... = Version 9: not supported (0x0)
704 .... .... 0... .... = Version 8: not supported (0x0)
705 .... .... .0.. .... = Version 7: not supported (0x0)
706 .... .... ..0. .... = Version 6: not supported (0x0)
707 .... .... ...0 .... = Version 5: not supported (0x0)
708 .... .... .... 0... = Version 4: not supported (0x0)
709 .... .... .... .0.. = Version 3: not supported (0x0)
710 .... .... .... ..0. = Version 2: not supported (0x0)
711 .... .... .... ...1 = Version 1: supported (0x1)
712 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
713 */
714 const uint8_t iuup_init[] = {
715 0xe0, 0x00, 0xdd, 0xb4, 0x06, 0x01, 0x51, 0x67, 0x3c, 0x86, 0x27,
716 0x00, 0x00, 0x00, 0x01, 0x00
717 };
718
719 iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
720 OSMO_ASSERT(iui);
721 osmo_iuup_instance_set_user_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_user_prim_cb, NULL);
722 osmo_iuup_instance_set_transport_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_transport_prim_cb, NULL);
723
724 clock_override_set(0, 0);
725
726 /* Tx CONFIG.req */
727 rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
728 rnp->u.config = def_configure_req;
729 rnp->u.config.active = false;
Vadim Yanitskiy9f5ddc42023-12-11 21:59:27 +0700730
731 rc = osmo_iuup_rnl_prim_down(iui, rnp);
732 OSMO_ASSERT(rc == 0);
Pau Espin Pedrol6d6d22e2022-05-27 14:30:17 +0200733
734 /* Send Init: */
735 tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
736 tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_init));
737 hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
738 memcpy(hdr14, iuup_init, sizeof(iuup_init));
Vadim Yanitskiy9f5ddc42023-12-11 21:59:27 +0700739
740 rc = osmo_iuup_tnl_prim_up(iui, tnp);
741 OSMO_ASSERT(rc == 0);
Pau Espin Pedrol6d6d22e2022-05-27 14:30:17 +0200742
743 osmo_iuup_instance_free(iui);
744}
745
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100746int main(int argc, char **argv)
747{
748 iuup_test_ctx = talloc_named_const(NULL, 0, "iuup_test");
749 osmo_init_logging2(iuup_test_ctx, NULL);
750 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
751 log_set_print_category(osmo_stderr_target, 1);
752 log_set_print_category_hex(osmo_stderr_target, 0);
753 log_set_use_color(osmo_stderr_target, 0);
754 log_set_category_filter(osmo_stderr_target, DLIUUP, 1, LOGL_DEBUG);
755 osmo_fsm_log_addr(false);
756
757 osmo_gettimeofday_override = true;
758
759 test_crc();
760 test_tinit_timeout_retrans();
761 test_init_nack_retrans();
762 test_init_ack();
763 test_passive_init();
Pau Espin Pedrolf62e01a2022-06-13 12:54:07 +0200764 test_passive_init_retrans();
Pau Espin Pedrol6d6d22e2022-05-27 14:30:17 +0200765 test_decode_passive_init_2_rfci_no_iptis();
Harald Welte9fe1f9f2018-11-29 13:47:39 +0100766
767 printf("OK.\n");
768}