1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
|
TTCN-3 and Eclipse TITAN for testing protocol stacks
====================================================
:author: Harald Welte <laforge@gnumonks.org>
:copyright: 2017 by Harald Welte (License: CC-BY-SA)
:backend: slidy
:max-width: 45em
== Protocol Testing
Important for:
* conformance to specification
* ensuring interoperability
* network security
* regression testing
* performance
== Protocol Testing
No standard methodology, language, approach, tool
* testing implementation against itself
** works only for symmetric protocols
** wouldn't cover lots of problems
* testing against wireshark
** wireshark often way more tolerant than spec
* custom implementation
** in Python (e.g. using scapy)
** in Erlang (good binary encoder/decoder) or other languages
* specific tools like packetdrill
== Protocol Testing
Personal story: During past years,
* I implemented tons of [telecom] protocols / stacks at Osmocom.org
* I was looking for better tools to help [automatic] testing
** primarily functional testing (correctness / conformance)
** not so much performance testing
* I figured Ideal test tool would...
** allow very productive and expressive way to describe encoding/decoding
** allow very convenient pattern matching on incoming messages
** allow exchange of messages asynchronously with implementation under test
* I stumbled on TTCN-3 occasionally and investigated
== The TTCN-3 Language
* domain-specific language *just* for protocol conformance tests
* TTCN history back to 1983 (!), TTCN-3 since 2000
* used extensively in classic telecom sector (Ericsson, Nokia, etc.)
* ETSI developed and published abstract test suites in TTCN-3 for
** IPv6, SIP, DIAMETER, ePassports, Digital Mobiel Radio, 6LoWPAN
* Other bodies published test suites for
** CoAP, MQTT, MOST, AUTOSAR
But: Until 2015, only proprietary tools / compilers :(
== Eclipse TITAN
* After TTCN-3 specification in 2000, Ericsson internally develops TTCN-3 toolchain
* adopted for many Ericsson-internal testing of all kinds of products
* proprietary software with commercial licenses
* 300,000 lines of Java + 1.6 Million lines of C++
* Released as Open Source as "Eclipse TITAN" in 2015
** Not just TTCN-3 compiler, but also extensive documentations and many protocol modules, test ports as well as Eclipse IDE, Log file viewer/visualizer, etc.
* `eclipse-titan` part of standard Debian / Ubuntu archive, only one apt-get away
Great, we can finally use TTCN-3 in FOSS!
== Eclipse TITAN compiler workflow
[graphviz]
----
digraph G {
progra [label="Human Developer"];
ttcn3 [label="TTCN-3 source (ATS)"];
cpp [label="Generated C++ source"];
exec [label="Binary Executable (ETS)"];
tps [label="Other C++ sources, as needed"];
progra -> ttcn3 [label="writes code"];
ttcn3 -> cpp [label="ttcn3_compiler"];
cpp -> exec [label="GNU gcc / g++"];
tps -> cpp
}
----
* TITAN actually _compiles_ into executable binaries, it is not using a VM or scripting
** ATS: Abstract Test Suite (source code)
** ETS: Executable Test Suite (executable code)
== TTCN-3 Language Features (with TITAN)
* comprehensive type system
* parametric templates
* variety of encoders/decoders
* automatic / comprehensive logging framework
* powerful program control statements
* built-in notion of tests cases, test suites, verdicts, ...
* runtime / executor for parallel test components + aggregating results
== TTCN-3 Basic Types
* Simple basic types such as `integer`, `float`, `boolen`
* Basic string types such as `bitstring`, `octetstring`, `hexstring`, `charstring` (IA5) and `universal charstring` (UCS-4).
* Structured Types `record`, `set`, `record of`, `set of`
* Verdict type `verdicttype`
** can have either value `none`, `pass`, `inconc`, `fail`, or `error`
** verdict can only _deteriorate_ (`pass` -> `fail`) but never improve (`error` -> `pass`)
** every test case implicitly has a verdict, no need to explicitly declare a variable of `verdicttype`
== TTCN-3 Structured Types
A structured type is an abstract type comprised of other types, whcih can be nested.
An example for a `record` type (similar to a C-language `struct`) is shown below
--------
type record MyMessageType {
integer field1 optional<1>,
charstring field2,
boolean field3
};
--------
<1> optional members may be present or not
== TTCN-3 Union Type
A union expresses a set of alternative types of which one alternative must be chosen.
--------
type union MyMessageUnion {
integer field1,
charstring field2,
};
--------
Difference to C-language union: `ischosen()` can be used to learn which of the union members is
chosen/defined!
== Not-used and omit
* until a variable or field of structured type is assigned, it is _unbound_
* whenever a _value_ is expected, TTCN-3 runtime will create an error for _unbound_
* in case of absence of optional fields, explicit `omit` value must be assigned!
== Sub-typing
Sub-typing can be used to further constrain a given type. Typical examples include constrained number ranges,
and string patterns
--------
type integer MyIntRange (1..100);
type integer MyIntRange8 (0..infinity);
type charstring MyCharRange (”k”..”w");
type charstring SideType (”left”, ”right”);
type integer MyIntListRange (1..5,7,9);
type record length(0..10) of integer RecOfInt;
type charstring CrLfTermStrin (pattern ”*\r\n”);
--------
== Templates
* Matching incoming messages against some kind of specification is one of the most common tasks in testing protocols
** some expected fields are static (message type)
** some expected fields are known (source address)
** some fields are chosen by sender (some identifier)
** some fields we don't care (optional headers that may or may not be present)
* TTCN-3 Templates provide elegant solution for this, avoiding any explicit code to be written
** templates can even be parametric, i.e. they can be instantiated with "arguments"
* templates can also be used for sending messages, if they are fully specified/qualified
== Templates
--------
// Value list template
template charstring tr_SingleABorC := (”A”, ”B”, ”C”);
--------
--------
// Value range
template float tr_NearPi := (3.14 .. 3.15);
template integer tr_FitsToOneByte := (0 .. 255);
template integer tr_GreaterThanZero := (1 .. infinity);
--------
--------
// Intermixed value list and range matching
template integer tr_Intermixed := ((0..127), 200, 255);
--------
== Matching inside values
--------
// Using any element matching inside a bitstring value
// Last 2 bits can be '0' or '1'
template bitstring tr_AnyBSValue := ’101101??’B;
--------
--------
// Matches charstrings with the first character "a"
// and the last one "z"
template charstring tr_0 := pattern "a*z";
--------
* more capabilities using `complement`, `ifpresent`, `subset`, `superset`, `permutation` constructs not
covered here
== Parametric Templates
See below for an example of a parametric template:
--------
type record MyMessageType {
integer field1 optional,
charstring field2,
boolean field3
};
template MyMessageType trMyTemplte(boolean pl_param) := {
field1 : = ?, // present, but any value
field2 : = (”B”, ”O”, ”Q”) ,
field3 := pl_param
};
--------
The built-in `match()` function can be used to check if a given value matches a given template. Some TTCN-3
statements such as `receive()` have built-in capabilities for template matching, avoiding even the explicit
call of `match()` in many cases.
== Template Hierarchy
Using modified templates, one can build a hierarchy of templates: From the specific to the unspecific
----
template MyMsgType t_MyMsgAny := {
msg_type := ?,
foo := bar
};
template MyMsgType t_MyMsg23 modifies t_MyMsgAny := {
msg_type := 23,
};
----
where
* _t_MyMsgAny_ matches a message with any message type and "foo=bar", while
* _t_MMyMsg23_ matches only those that have "foo=bar" and "msg_type=23"
== Encoders/Decoders
* type system, templates, matching are all nice and great, but we need to get data from wire format into
TTCN-3 abstract types
* TTTCN-3 specifies importing of formal schema definitios, such as ASN.1, IDL, XSD (XML) and JSON
* TITAN has additional codecs for those (many) protocols that lack formal syntax
** `raw` codec for binary protocols (e.g. GTP)
** `text` codec for text based protocols (e.g. HTTP, MGCP, IMAP, ...)
* codecs allow you to _express/describe_ the format (declarative programming) rather than the usual imperative approach
== TITAN raw codec: UDP Example
How to express an UDP header using TITAN raw codec
--------
type integer LIN2_BO_LAST (0..65535) with {
variant ”FIELDLENGTH(16), COMP(nosign), BYTEORDER(last)”
};
type record UDP_header {
LIN2_BO_LAST srcport,
LIN2_BO_LAST dstport,
LIN2_BO_LAST len,
LIN2_BO_LAST cksum
} with { variant ”FIELDORDER(msb)” };
type record UDP packet {
UDP_header header
octetstring payload
} with {
variant (header) ”LENGTHTO(header, payload), LENGTHINDEX(len)”
};
--------
== TITAN raw codec: GTP Example
How to express an GTP header using TITAN raw codec
--------
type record GRE_Header {
BIT1 csum_present,
BIT1 rt_present,
BIT1 key_present,
...
OCT2 protocol_type,
OCT2 checksum optional,
OCT2 offset optional,
OCT4 key otional,
...
} with {
variant (checksum) "PRESENCE(csum_present='1', rt_present='1'B)"
variant (offset) "PRESENCE(csum_present='1'B, rt_present='1'B)"
variant (key) "PRESENCE(key_present='1'B)"
}
--------
== TITAN text codec: MGCP Example
--------
type charstring MgcpVerb ("EPCF", "CRCX", "MDCX", "DLCX", "RQNT", "NTFY",
"AUEP", "AUCX", "RSIP") with {
variant "TEXT_CODING(,convert=upper_case,,case_insensitive)"
};
type charstring MgcpTransId (pattern "\d#(1,9)");
type charstring MgcpEndpoint (pattern "*@*");
type charstring MgcpVersion (pattern "\d.\d") with {
variant "BEGIN('MGCP ')"
};
type record MgcpCommandLine {
MgcpVerb verb,
MgcpTransId trans_id,
MgcpEndpoint ep,
MgcpVersion ver
} with {
variant "SEPARATOR(' ', '[\t ]+')"
variant "END('\r\n', '([\r\n])|(\r\n)')"
};
--------
== Program Control Statements
* `if` / `else` like in C
* `select` statement similar to C `switch`
* `for`, `while`, `do-while` loops like in C
* `goto` and `label`
* `break` and `continue` like in C
== Abstract Communications Operations
* TTCN-3 test suites communicate with _implementation under test_ through abstract TestPorts
** TestPorts can be implemented in TTCN-3 or C++ and linked in
** TestPorts must be _connected_ before using send/receive operaitons
** TITAN provides TestPorts for e.g. packet socket, IP/UDP/TCP/SCTP socket, ...
* `<port>.send(<ValueRef>)` performs non-blocking send
** Literal value, constant, variable, specific value template, ...
* `<port>.receive(<TemplateRef>)` or `<port>.receive` performs blocking receive
** literal value, constant, variable, template (with matching!), inline template
'... but if receive blocks, how can we wait for any of N events?
== Program Control and Behavior
* program statements are executed in order
* blocking statements block the execution of the component
* occurrence of unexpected event may cause infinite blocking
----
// x must be the first on queue P, y the second
P.receive(x); // Blocks until x appears on top of queue P
P.receive(y); // Blocks until y appears on top of queue P
// When y arrives first then P.receive(x) blocks -> error
----
This is what leads to the `alt` statement:
`alt` declares a seto alternatives covering all events, which
* can happen: expected messages, timeouts, ...
* must not happen: unexpected faulty messages, no message received, ...
* all alternatives inside `alt` are blocking operations
== The `alt` statement
----
P.send(req)
T.start;
// ...
alt {
[] P.receive(resp) { /* actions to do and exit alt */ }
[] any port.receive { /* handle unexpected event */ }
[] T.timeout { /* handle timer expiry and exit */ }
}
----
* [] is guard condition enables or disables the alternative
** usually empty `[]` equals `[true]`
** can contain a condition like `[x > 0]`
** very good for e.g. state machines to activate some alternatives only in certain states while others may
occur in any state
== The `alt` and `repeat` statements
The `repeat` statement
* takes a new snapshot and re-evaluates the alt statement
* can appear as last statement in statement blocks of statements
----
P.send(req)
T.start;
alt {
[] P.receive(resp) { /* actions to do and exit alt */ }
[] P.receive(keep_alive) { /* handle keep alive message */
repeat }
[] any port.receive { /* handle unexpected event */ }
[] T.timeout { /* handle timer expiry and exit */ }
}
----
== TTCN-3 modules
TTCN-3 code is written in _modules_
* a test suite consists of one or more modules
* a module contains _module definitions_ and an optional _control part_
** _parameters_ (automatically configurable via config file)
** definition of _data types_, _constants_, _templates_
** definition of _communications ports_
** definition of _test components_, _functions_ _altstesp_ and _test cases_
** _control part_ determines default order/execution of test cases
* modules can import from each other (think in python terms)
== Examples
Let's have a look at some real-world examples and do a bit of a walk-through
before continuing with the slides...
== Logging
* TITAN runtime contains extensive logging framework
* config file determines log level for various different subsystems
** e.g. any encode, decode, receive, transmit operations logged
** timer starts, expirations
** any changes to test case verdict
* explicit logging from code by use of `log()` built-in function
* `ttcn3_logformat` tool for pretty-printing log files
* `ttcn3_logmerge` tool for merging/splicing multiple logs
* log plugins e.g. for generating JUnit-XML available
** facilitates easy reporting / integration to Jenkins or other CI
== Logging
Log file format example:
----
// abstract data type before encode
13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : { peer := { connId := 1, remName := "127.0.23.1", remPort := 2123 }, gtpc := { pn_bit := '0'B, s_bit := '1'B, e_bit := '0'B, spare := '0'B, pt := '1'B, version := '001'B, messageType := '01'O, lengthf := 0, teid := '00000000'O, opt_part := { sequenceNumber := '3AAC'O, npduNumber := '00'O, nextExtHeader := '00'O, gTPC_extensionHeader_List := omit }, gtpc_pdu := { echoRequest := { private_extension_gtpc := omit } } } }
// 'msg' contains encoded binary data actually sent via socket
13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : { connId := 1, remName := "127.0.23.1", remPort := 2123, proto := { udp := { } }, msg := '32010004000000003AAC0000'O }
----
== Logging
The same log file lines if run through `ttcn3_logformat`
----
13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : {
peer := {
connId := 1,
remName := "127.0.23.1",
remPort := 2123
},
gtpc := {
pn_bit := '0'B,
s_bit := '1'B,
e_bit := '0'B,
spare := '0'B,
pt := '1'B,
version := '001'B,
messageType := '01'O,
lengthf := 0,
teid := '00000000'O,
opt_part := {
sequenceNumber := '3AAC'O,
npduNumber := '00'O,
nextExtHeader := '00'O,
gTPC_extensionHeader_List := omit
},
gtpc_pdu := {
echoRequest := {
private_extension_gtpc := omit
}
}
}
}
13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : {
connId := 1,
remName := "127.0.23.1",
remPort := 2123,
proto := {
udp := { }
},
msg := '32010004000000003AAC0000'O
}
----
== Existing TITAN Source
* Protocol encoding/decoding
** BSSAP+, BSSGP, BSSMAP, CoAP, DSS1, DUA, EAP, GRE, GTP, HTTP, ISUP, LLC, M2PA, M2UA, MQTT, MongoDB, NDP, NS,
NTAF, ROSE, SCTP, SDP, SNDCP, STOMP, STUN, SUA, TLS, WTP, DNS, IP, SMPP, SNMP, IKEv2, DHCP, PPP, RTP, TCP,
UDP, XMPP, DHCPv6, SMTP, ICMP, RTSP, ICMPv6, DIAMETER, FrameRelay, ProtoBuff, IUA, L2TP, M3UA, MIME,
WebSocket, H.248, IMAP, IPsec, SRTP, MSRP, ICAP, RADIUS
* Protocol Emulation
** M3UA, SCCP, SUA
* Test Ports
** GPIO, MTP3, Serial, SocketCAN, SCTP, SIP, HTTP, Telnet, UDP, pcap file, pipe, SQL, TCP, SUNRPC, SSH, STDINOUT, sockets, LDAP
== Further Reading
* Ericsson TTCN-3 tutorial http://www.ttcn-3.org/files/TTCN3_P.pdf
* An Introduction to TTCN-3, 2nd Edition <http://www.wiley.com/go/willcock_TTCN-3_2e>
* Modules https://github.com/eclipse
* More Modules http://git.eclipse.org/
* Debian https://packages.debian.org/search?keywords=eclipse-titan
* Ubuntu https://packages.ubuntu.com/search?keywords=eclipse-titan
== EOF
End of File
|