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