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