#include #include #include #include #include #include #include #include #include #include static void *iuup_test_ctx; static struct osmo_iuup_rnl_config def_configure_req = { .transparent = false, .active = true, .supported_versions_mask = 0x0001, .num_rfci = 3, .num_subflows = 3, .subflow_sizes = { {81, 103, 60}, {39, 0, 0}, {0, 0, 0}, }, /* .delivery_err_sdu = All set to 0 (YES) by default, */ .IPTIs_present = true, .IPTIs = {1, 7, 1}, .t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT }, .t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT }, .t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT }, }; /* Frame 33, "Initialization", OS#4744 3g_call_23112021.pcapng IuUP 1110 .... = PDU Type: Control Procedure (14) .... 00.. = Ack/Nack: Procedure (0) .... ..00 = Frame Number: 0 0000 .... = Mode Version: 0x0 .... 0000 = Procedure: Initialization (0) 1101 11.. = Header CRC: 0x37 [correct] .... ..11 1001 1001 = Payload CRC: 0x399 000. .... = Spare: 0x0 ...1 .... = TI: IPTIs present in frame (1) .... 011. = Subflows: 3 .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0) RFCI 0 Initialization 0... .... = RFCI 0 LRI: Not last RFCI (0x0) .0.. .... = RFCI 0 LI: one octet used (0x0) ..00 0000 = RFCI 0: 0 RFCI 0 Flow 0 Len: 81 RFCI 0 Flow 1 Len: 103 RFCI 0 Flow 2 Len: 60 RFCI 1 Initialization 0... .... = RFCI 1 LRI: Not last RFCI (0x0) .0.. .... = RFCI 1 LI: one octet used (0x0) ..00 0001 = RFCI 1: 1 RFCI 1 Flow 0 Len: 39 RFCI 1 Flow 1 Len: 0 RFCI 1 Flow 2 Len: 0 RFCI 2 Initialization 1... .... = RFCI 2 LRI: Last RFCI in current frame (0x1) .0.. .... = RFCI 2 LI: one octet used (0x0) ..00 0010 = RFCI 2: 2 RFCI 2 Flow 0 Len: 0 RFCI 2 Flow 1 Len: 0 RFCI 2 Flow 2 Len: 0 IPTIs 0001 .... = RFCI 0 IPTI: 0x1 .... 0111 = RFCI 1 IPTI: 0x7 0001 .... = RFCI 2 IPTI: 0x1 Iu UP Mode Versions Supported: 0x0001 0... .... .... .... = Version 16: not supported (0x0) .0.. .... .... .... = Version 15: not supported (0x0) ..0. .... .... .... = Version 14: not supported (0x0) ...0 .... .... .... = Version 13: not supported (0x0) .... 0... .... .... = Version 12: not supported (0x0) .... .0.. .... .... = Version 11: not supported (0x0) .... ..0. .... .... = Version 10: not supported (0x0) .... ...0 .... .... = Version 9: not supported (0x0) .... .... 0... .... = Version 8: not supported (0x0) .... .... .0.. .... = Version 7: not supported (0x0) .... .... ..0. .... = Version 6: not supported (0x0) .... .... ...0 .... = Version 5: not supported (0x0) .... .... .... 0... = Version 4: not supported (0x0) .... .... .... .0.. = Version 3: not supported (0x0) .... .... .... ..0. = Version 2: not supported (0x0) .... .... .... ...1 = Version 1: supported (0x1) 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0) */ static const uint8_t iuup_initialization[] = { 0xe0, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00 }; /* Frame 87, "Data RFCI=0 FN = 1", OS#4744 3g_call_23112021.pcapng IuUP 0000 .... = PDU Type: Data with CRC (0) .... 0001 = Frame Number: 1 00.. .... = FQC: Frame Good (0) ..00 0000 = RFCI: 0x00 1110 00.. = Header CRC: 0x38 [correct] .... ..11 1111 1111 = Payload CRC: 0x3ff Payload Data: 08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740 */ static const uint8_t iuup_data[] = { 0x01, 0x00, 0xe3, 0xff, /*payload starts here: */ 0x08, 0x55, 0x6d, 0x94, 0x4c, 0x71, 0xa1, 0xa0, 0x81, 0xe7, 0xea, 0xd2, 0x04, 0x24, 0x44, 0x80, 0x00, 0x0e, 0xcd, 0x82, 0xb8, 0x11, 0x18, 0x00, 0x00, 0x97, 0xc4, 0x79, 0x4e, 0x77, 0x40 }; #define IUUP_MSGB_SIZE 4096 static struct osmo_iuup_tnl_prim *itp_ctrl_nack_alloc(enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn) { struct osmo_iuup_tnl_prim *tnp; struct iuup_ctrl_nack *nack; tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE); tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_nack)); nack = (struct iuup_ctrl_nack *) msgb_l2(tnp->oph.msg); *nack = (struct iuup_ctrl_nack){ .hdr = { .frame_nr = fn, .ack_nack = IUUP_AN_NACK, .pdu_type = IUUP_PDU_T_CONTROL, .proc_ind = proc_ind, .mode_version = 0, .payload_crc_hi = 0, .header_crc = 0, .payload_crc_lo = 0, }, .spare = 0, .error_cause = error_cause, }; nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg)); return tnp; } static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(enum iuup_procedure proc_ind, uint8_t fn) { struct osmo_iuup_tnl_prim *tnp; struct iuup_ctrl_ack *ack; tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE); tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_ack)); ack = (struct iuup_ctrl_ack *) msgb_l2(tnp->oph.msg); *ack = (struct iuup_ctrl_ack){ .hdr = { .frame_nr = fn, .ack_nack = IUUP_AN_ACK, .pdu_type = IUUP_PDU_T_CONTROL, .proc_ind = proc_ind, .mode_version = 0, .payload_crc_hi = 0, .header_crc = 0, .payload_crc_lo = 0, }, }; ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg)); return tnp; } static void clock_override_set(long sec, long usec) { osmo_gettimeofday_override_time.tv_sec = sec + usec / (1000*1000); osmo_gettimeofday_override_time.tv_usec = usec % (1000*1000); printf("sys={%lu.%06lu}, %s\n", osmo_gettimeofday_override_time.tv_sec, osmo_gettimeofday_override_time.tv_usec, __func__); } void test_crc(void) { int rc; /* Frame 34, "Initialization ACK", OS#4744 3g_call_23112021.pcapng */ static const uint8_t iuup_initialization_ack[] = { 0xe4, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00 }; printf("=== start: %s ===\n", __func__); rc = osmo_iuup_compute_header_crc(iuup_initialization, sizeof(iuup_initialization)); printf("iuup_initialization: Header CRC = 0x%02x\n", rc); rc = osmo_iuup_compute_payload_crc(iuup_initialization, sizeof(iuup_initialization)); printf("iuup_initialization: Payload CRC = 0x%03x\n", rc); rc = osmo_iuup_compute_header_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack)); printf("iuup_initialization_ack: Header CRC = 0x%02x\n", rc); rc = osmo_iuup_compute_payload_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack)); printf("iuup_initialization_ack: Payload CRC = 0x%03x\n", rc); printf("=== end: %s ===\n", __func__); } /**************************** * test_tinit_timeout_retrans ****************************/ static unsigned int _tinit_timeout_retrans_user_rx_prim = 0; static int _tinit_timeout_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION)); OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT); OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR); OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_LOCAL); _tinit_timeout_retrans_user_rx_prim++; msgb_free(oph->msg); return 0; } static unsigned int _tinit_timeout_retrans_transport_rx_prim = 0; static int _tinit_timeout_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; struct msgb *msg = oph->msg; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); printf("Transport: DL len=%u: %s\n", msgb_l2len(msg), osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg))); _tinit_timeout_retrans_transport_rx_prim++; msgb_free(msg); return 0; } void test_tinit_timeout_retrans(void) { struct osmo_iuup_instance *iui; struct osmo_iuup_rnl_prim *rnp; int rc, i; iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__); OSMO_ASSERT(iui); osmo_iuup_instance_set_user_prim_cb(iui, _tinit_timeout_retrans_user_prim_cb, NULL); osmo_iuup_instance_set_transport_prim_cb(iui, _tinit_timeout_retrans_transport_prim_cb, NULL); clock_override_set(0, 0); /* Tx CONFIG.req */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.config = def_configure_req; OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); /* STATUS-INIT.req is transmitted automatically: */ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == 1); /* After one sec, INITIALIZATION msg is retransmitted */ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) { clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i); osmo_select_main(0); OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i + 1); } /* Last one should send an error event: */ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 0); clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i); osmo_select_main(0); OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i); OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1); /* Nothing else is received afterwards. osmo_select_main() will block forever. */ /*clock_override_set(i + 1, 0); osmo_select_main(0); OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i); OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);*/ osmo_iuup_instance_free(iui); } /**************************** * test_tinit_nack ****************************/ static unsigned int _init_nack_retrans_user_rx_prim = 0; static int _init_nack_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION)); OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT); OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK); OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_SECOND_FWD); _init_nack_retrans_user_rx_prim++; msgb_free(oph->msg); return 0; } static int _init_nack_retrans_transport_rx_prim = 0; static int _init_nack_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; struct msgb *msg = oph->msg; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); printf("Transport: DL len=%u: %s\n", msgb_l2len(msg), osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg))); _init_nack_retrans_transport_rx_prim++; msgb_free(msg); return 0; } void test_init_nack_retrans(void) { struct osmo_iuup_instance *iui; struct osmo_iuup_rnl_prim *rnp; struct osmo_iuup_tnl_prim *tnp; int rc, i; iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__); OSMO_ASSERT(iui); osmo_iuup_instance_set_user_prim_cb(iui, _init_nack_retrans_user_prim_cb, NULL); osmo_iuup_instance_set_transport_prim_cb(iui, _init_nack_retrans_transport_prim_cb, NULL); clock_override_set(0, 0); /* Tx CONFIG.req */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.config = def_configure_req; OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); /* STATUS-INIT.req is transmitted automatically: */ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == 1); /* After one sec, INITIALIZATION msg is retransmitted */ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) { /* Send NACK: */ tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); /* A new INIT is retransmitted: */ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i + 1); } /* Last one should send an error event: */ OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 0); tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i); OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 1); /* Nothing else is received afterwards. osmo_select_main() will block forever. */ osmo_iuup_instance_free(iui); } /**************************** * test_init_ack ****************************/ static unsigned int _init_ack_user_rx_prim = 0; static int _init_ack_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; struct msgb *msg = oph->msg; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION)); printf("User: UL len=%u: %s\n", msgb_l3len(msg), osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg))); _init_ack_user_rx_prim++; msgb_free(oph->msg); return 0; } static int _init_ack_transport_rx_prim = 0; static int _init_ack_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; struct msgb *msg = oph->msg; printf("%s()\n", __func__); OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); printf("Transport: DL len=%u: %s\n", msgb_l2len(msg), osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg))); _init_ack_transport_rx_prim++; msgb_free(msg); return 0; } void test_init_ack(void) { struct osmo_iuup_instance *iui; struct osmo_iuup_rnl_prim *rnp; struct osmo_iuup_tnl_prim *tnp; struct iuup_pdutype0_hdr *hdr0; int rc; iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__); OSMO_ASSERT(iui); osmo_iuup_instance_set_user_prim_cb(iui, _init_ack_user_prim_cb, NULL); osmo_iuup_instance_set_transport_prim_cb(iui, _init_ack_transport_prim_cb, NULL); clock_override_set(0, 0); /* Tx CONFIG.req */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.config = def_configure_req; OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); /* STATUS-INIT.req is transmitted automatically: */ OSMO_ASSERT(_init_ack_transport_rx_prim == 1); /* Send ACK: */ tnp = itp_ctrl_ack_alloc(IUUP_PROC_INIT, 0); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); OSMO_ASSERT(_init_ack_transport_rx_prim == 1); /* Make sure there's no retrans */ OSMO_ASSERT(_init_ack_user_rx_prim == 0); /* Make sure there's no error event */ /* Send IuUP incoming data to the implementation: */ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE); tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data)); hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg); memcpy(hdr0, iuup_data, sizeof(iuup_data)); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); /* We receive it in RNL: */ OSMO_ASSERT(_init_ack_user_rx_prim == 1); /* Now in opposite direction, RNL->[IuuP]->TNL: */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.data.rfci = 0; rnp->u.data.frame_nr = 1; rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD; rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4); memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4); OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); OSMO_ASSERT(_init_ack_transport_rx_prim == 2); /* We receive data in TNL */ osmo_iuup_instance_free(iui); } /**************************** * test_passive_init ****************************/ static unsigned int _passive_init_user_rx_prim = 0; static int _passive_init_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; struct msgb *msg = oph->msg; printf("%s()\n", __func__); switch (_passive_init_user_rx_prim) { case 0: OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION)); OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT); break; case 1: default: OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION)); printf("User: UL len=%u: %s\n", msgb_l3len(msg), osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg))); } _passive_init_user_rx_prim++; msgb_free(oph->msg); return 0; } static int _passive_init_transport_rx_prim = 0; static int _passive_init_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; struct msgb *msg; printf("%s()\n", __func__); msg = oph->msg; OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); printf("Transport: DL len=%u: %s\n", msgb_l2len(msg), osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg))); _passive_init_transport_rx_prim++; msgb_free(msg); return 0; } void test_passive_init(void) { /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */ struct osmo_iuup_instance *iui; struct osmo_iuup_rnl_prim *rnp; struct osmo_iuup_tnl_prim *tnp; struct iuup_pdutype14_hdr *hdr14; struct iuup_pdutype0_hdr *hdr0; int rc; iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__); OSMO_ASSERT(iui); osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_user_prim_cb, NULL); osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL); clock_override_set(0, 0); /* Tx CONFIG.req */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.config = def_configure_req; rnp->u.config.active = false; OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); /* STATUS-INIT.req is NOT transmitted automatically: */ OSMO_ASSERT(_passive_init_transport_rx_prim == 0); /* Send Init: */ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE); tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization)); hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg); memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization)); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */ OSMO_ASSERT(_passive_init_user_rx_prim == 1); /* We receive the Status-Init.ind */ /* Send IuUP incoming data to the implementation: */ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE); tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data)); hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg); memcpy(hdr0, iuup_data, sizeof(iuup_data)); OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0); /* We receive it in RNL: */ OSMO_ASSERT(_passive_init_user_rx_prim == 2); /* Now in opposite direction, RNL->[IuuP]->TNL: */ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE); rnp->u.data.rfci = 0; rnp->u.data.frame_nr = 1; rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD; rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4); memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4); OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0); OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive data in TNL */ osmo_iuup_instance_free(iui); } int main(int argc, char **argv) { iuup_test_ctx = talloc_named_const(NULL, 0, "iuup_test"); osmo_init_logging2(iuup_test_ctx, NULL); log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); log_set_print_category(osmo_stderr_target, 1); log_set_print_category_hex(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0); log_set_category_filter(osmo_stderr_target, DLIUUP, 1, LOGL_DEBUG); osmo_fsm_log_addr(false); osmo_gettimeofday_override = true; test_crc(); test_tinit_timeout_retrans(); test_init_nack_retrans(); test_init_ack(); test_passive_init(); printf("OK.\n"); }