blob: c4097a5f8ed9f1cd1d10d8f7588ef3aba296b013 [file] [log] [blame]
Stefan Sperling0796a822018-10-05 13:01:39 +02001/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
2 * Author: Stefan Sperling <ssperling@sysmocom.de>
3 * All Rights Reserved
4 *
5 * Released under the terms of GNU General Public License, Version 2 or
6 * (at your option) any later version.
Harald Welte34b5a952019-05-27 11:54:11 +02007 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
Stefan Sperling0796a822018-10-05 13:01:39 +02009 */
10
11/*
12 * This module provides functions which implement IPA protocol tests.
13 * There are no test cases defined here. Instead, there are test functions which
14 * can be called by test cases in our test suites. Each such function will create
15 * an IPA_CT component and execute a test on this component, and expects destination
16 * IP address, TCP port, and connection mode parameters. Depending on the connection
17 * mode, a test function will either connect to an IPA server on the specified
18 * address and port, or listen for an IPA client on the specified address and port.
19 * This allows IPA tests to be run against any IPA speakers used by various test suites.
20 */
21
22module IPA_Testing {
23
24import from IPL4asp_Types all;
25import from IPL4asp_PortType all;
26import from IPA_Types all;
27import from Osmocom_Types all;
28
29type enumerated IPA_ConnectionMode {
30 CONNECT_TO_SERVER,
31 LISTEN_FOR_CLIENT
32};
33
34/* Encoded IPA messages (network byte order) */
35const octetstring ipa_msg_ping := '0001FE00'O;
36const octetstring ipa_msg_pong := '0001FE01'O;
Stefan Sperlingaa1e60f2018-10-15 16:34:07 +020037const octetstring ipa_msg_id_req_hdr := '0007FE'O;
38const octetstring ipa_msg_id_req_payload := '04010801070102'O;
Stefan Sperling0796a822018-10-05 13:01:39 +020039
40/* A component which represents the system on which the IPA speaker is running. */
41type component system_CT {
42 port IPL4asp_PT IPL4;
43}
44
45/* Main component provided by this module. */
46type component IPA_CT {
47 port IPL4asp_PT IPL4;
48 timer g_Tguard;
49}
50
51/* This guard timer prevents us from waiting too long if the IPA TCP connection hangs. */
52private altstep as_Tguard() runs on IPA_CT {
53 [] g_Tguard.timeout {
54 setverdict(fail, "Tguard timeout");
55 mtc.stop;
56 }
57}
58
59/* Send an encoded IPA message across an IPA TCP connection. */
60private function f_send_ipa_data(charstring ipa_ip, integer ipa_tcp_port, ConnectionId connId,
61 octetstring data) runs on IPA_CT {
62 var IPL4asp_Types.Result res;
63 var ASP_SendTo asp := {
64 connId := connId,
65 remName := ipa_ip,
66 remPort := ipa_tcp_port,
67 proto := {tcp := {}},
68 msg := data
69 };
70 IPL4.send(asp);
71}
72
73/* Match an incoming IPA message. */
74private template ASP_RecvFrom t_recvfrom(template octetstring msg) := {
75 connId := ?,
76 remName := ?,
77 remPort := ?,
78 locName := ?,
79 locPort := ?,
80 proto := {tcp := {}},
81 userData := ?,
82 msg := msg
83}
84
85/* Perform set up steps for a test function. */
86private function f_init(charstring ipa_ip, integer ipa_tcp_port,
87 IPA_ConnectionMode conmode) runs on IPA_CT return ConnectionId {
88 var IPL4asp_Types.Result res;
89 var ConnectionId connId;
90
91 map(self:IPL4, system:IPL4);
92 if (conmode == CONNECT_TO_SERVER) {
93 /* Create an IPA connection over TCP. */
94 res := IPL4asp_PortType.f_IPL4_connect(IPL4, ipa_ip, ipa_tcp_port, "", -1, 0, {tcp := {}});
95 if (not ispresent(res.connId)) {
96 setverdict(fail, "Could not connect IPA socket to ", ipa_ip, " port ",
97 ipa_tcp_port, "; check your configuration");
98 mtc.stop;
99 }
100 } else {
101 /* Listen for an incoming IPA connection on TCP. */
102 res := IPL4asp_PortType.f_IPL4_listen(IPL4, ipa_ip, ipa_tcp_port, {tcp := {}});
103 if (not ispresent(res.connId)) {
104 setverdict(fail, "Could not listen on address ", ipa_ip, " port ",
105 ipa_tcp_port, "; check your configuration");
106 mtc.stop;
107 }
108 }
109
110 /*
111 * Activate guard timer. When changing the timeout value, keep in mind
112 * that test functions below may wait for some amount of time, which
113 * this guard timer should always exceed to avoid spurious failures.
114 */
115 g_Tguard.start(60.0);
116 activate(as_Tguard());
117
118 return res.connId;
119}
120
121/*
122 * Individual test case implementations.
123 */
124
125private function f_send_chopped_ipa_msg(charstring ipa_ip, integer ipa_tcp_port, ConnectionId connId,
126 octetstring msg) runs on IPA_CT {
127 const float delay := 6.0;
128 for (var integer i := 0; i < lengthof(msg); i := i + 1) {
129 log("sending byte ", msg[i]);
130 f_send_ipa_data(ipa_ip, ipa_tcp_port, connId, msg[i]);
131 f_sleep(delay);
132 }
133}
134
135/* Send a ping message one byte at a time, waiting for TCP buffer to flush between each byte. */
136private function f_TC_chopped_ipa_ping(charstring ipa_ip, integer ipa_tcp_port,
137 IPA_ConnectionMode conmode) runs on IPA_CT system system_CT {
138 var ConnectionId connId;
139 var ASP_RecvFrom asp_rx;
140
141 connId := f_init(ipa_ip, ipa_tcp_port, conmode);
142
143 if (conmode == CONNECT_TO_SERVER) {
144 f_send_chopped_ipa_msg(ipa_ip, ipa_tcp_port, connId, ipa_msg_ping);
145 } else {
Stefan Sperling0ec1c262018-10-15 15:12:52 +0200146 var PortEvent port_evt;
147 IPL4.receive(PortEvent:{connOpened := ?}) -> value port_evt {
148 var ConnectionOpenedEvent conn := port_evt.connOpened;
149 f_send_chopped_ipa_msg(conn.remName, conn.remPort, conn.connId, ipa_msg_ping);
Stefan Sperling0796a822018-10-05 13:01:39 +0200150 }
151 }
152
153 /* Expect a pong response. */
154 alt {
155 [] IPL4.receive(t_recvfrom(ipa_msg_pong)) -> value asp_rx {
156 log("received pong from ", asp_rx.remName, " port ", asp_rx.remPort, ": ", asp_rx.msg);
157 setverdict(pass);
158 }
159 [] IPL4.receive {
160 repeat;
161 }
162 }
163}
164
Stefan Sperlingaa1e60f2018-10-15 16:34:07 +0200165/* Send a complete IPA "ID REQ" message header in one piece, and then send the payload one byte at a time,
166 * waiting for TCP buffer to flush between each byte. */
167private function f_TC_chopped_ipa_payload(charstring ipa_ip, integer ipa_tcp_port,
168 IPA_ConnectionMode conmode) runs on IPA_CT system system_CT {
169 var ConnectionId connId;
170 var ASP_RecvFrom asp_rx;
171
172 connId := f_init(ipa_ip, ipa_tcp_port, conmode);
173
174 if (conmode == CONNECT_TO_SERVER) {
175 var PortEvent port_evt;
176 f_send_ipa_data(ipa_ip, ipa_tcp_port, connId, ipa_msg_id_req_hdr);
177 f_send_chopped_ipa_msg(ipa_ip, ipa_tcp_port, connId, ipa_msg_id_req_payload);
178 /* Server will close the connection upon receiving an ID REQ. */
179 alt {
180 [] IPL4.receive(PortEvent:{connClosed := ?}) -> value port_evt {
181 if (port_evt.connClosed.connId == connId) {
182 setverdict(pass);
183 } else {
184 repeat;
185 }
186 }
187 [] IPL4.receive {
188 repeat;
189 }
190 }
191 } else {
192 var PortEvent port_evt;
193 IPL4.receive(PortEvent:{connOpened := ?}) -> value port_evt {
194 var ConnectionOpenedEvent conn := port_evt.connOpened;
195 f_send_ipa_data(conn.remName, conn.remPort, conn.connId, ipa_msg_id_req_hdr);
196 f_send_chopped_ipa_msg(conn.remName, conn.remPort, conn.connId, ipa_msg_id_req_payload);
197 }
198
199 /* Expect an encoded IPA ID RESP message from the client. */
200 alt {
201 [] IPL4.receive(t_recvfrom(?)) -> value asp_rx {
202 log("received IPA message from ", asp_rx.remName, " port ", asp_rx.remPort, ": ",
203 asp_rx.msg);
204 if (lengthof(asp_rx.msg) > 4
205 and asp_rx.msg[2] == 'FE'O /* PROTO_IPACCESS */
206 and asp_rx.msg[3] == '05'O /* ID RESP */) {
207 setverdict(pass);
208 } else {
209 repeat;
210 }
211 }
212 [] IPL4.receive {
213 repeat;
214 }
215 }
216 }
217}
218
Stefan Sperling0796a822018-10-05 13:01:39 +0200219/*
220 * Public functions.
221 * Test suites may call these functions to create an IPA_CT component and run a test to completion.
222 */
223
224function f_run_TC_chopped_ipa_ping(charstring ipa_ip, integer ipa_tcp_port, IPA_ConnectionMode conmode) {
225 var IPA_Testing.IPA_CT vc_IPA_Testing := IPA_Testing.IPA_CT.create;
226 vc_IPA_Testing.start(IPA_Testing.f_TC_chopped_ipa_ping(ipa_ip, ipa_tcp_port, conmode));
227 vc_IPA_Testing.done;
228}
229
Stefan Sperlingaa1e60f2018-10-15 16:34:07 +0200230function f_run_TC_chopped_ipa_payload(charstring ipa_ip, integer ipa_tcp_port, IPA_ConnectionMode conmode) {
231 var IPA_Testing.IPA_CT vc_IPA_Testing := IPA_Testing.IPA_CT.create;
232 vc_IPA_Testing.start(IPA_Testing.f_TC_chopped_ipa_payload(ipa_ip, ipa_tcp_port, conmode));
233 vc_IPA_Testing.done;
234}
235
Stefan Sperling0796a822018-10-05 13:01:39 +0200236}