aboutsummaryrefslogtreecommitdiffstats
path: root/library/SABP_Adapter.ttcn
blob: e94e91cb8625630644917d671d4b58374cd7870a (plain)
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
module SABP_Adapter {

/* SABP Adapter layer, sitting on top of SABP_CodecPort.
 * test suites can 'inherit' in order to have a SABP connection to the IUT which they're testing
 *
 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 */


import from Osmocom_Types all;
import from General_Types all;
import from SABP_Types all;
import from SABP_PDU_Descriptions all;
import from SABP_Templates all;
import from SABP_CodecPort all;
import from SABP_CodecPort_CtrlFunct all;
import from IPL4asp_Types all;
import from IPL4asp_PortType all;
//import from Socket_API_Definitions all;

const integer SABP_HDR_LEN := 3;

const integer NUM_SABP := 3;

type component SABP_Adapter_CT {
	/* down-facing port to SABP Codec port */
	port SABP_CODEC_PT SABP[NUM_SABP];
	var IPL4asp_Types.ConnectionId g_sabp_conn_id[NUM_SABP] := { -1, -1, -1 };
}

/*! parse a single APER length determinant. Return -1 if input insufficient or -2 if invalid */
private function f_aper_len_det(in octetstring stream, out integer len_len) return integer {
	if (lengthof(stream) < 1) {
		return -1;
	}

	select (stream[0] and4b 'C0'O) {
	case ('00'O) {
		/* total length is encoded in this octet */
		len_len := 1;
		return oct2int(stream[0]);
		}
	case ('80'O) {
		/* total length (up to 16k) encoded in two octets */
		if (lengthof(stream) < 2) {
			return -1;
		}
		len_len := 2;
		return (oct2int(stream[0] and4b '3F'O) * 256) + oct2int(stream[1]);
		}
	case ('C0'O) {
		/* total length not known, encoded in chunks; first chunk length now known */
		len_len := 1;
		return oct2int(stream[0] and4b '3F'O) * 16384;
		}
	case else {
		return -2;
		}

	}
}

/* The callback function has to return the length of the message if completely received. It has to return
 * "-1" if the length cannot be determined. If the message is incomplete, but the length can be
 * determined, then the function should return the length. In this case the callback function will not be
 * called again for the given message - possibly increasing the performance. Alternatively the function may
 * always return "-1" when the message is incomplete.
 * If the callback function detects that the it will be impossible to determine the length of the message,
 * even receiving more octets, should return "-2". In this case the connection will be closed and the
 * length calculation error will be reported. */
private function f_APER_getMsgLen(in octetstring stream, inout ro_integer args) return integer {
	var integer stream_len := lengthof(stream);
	var integer hdr_len := args[0];
	var octetstring stream_nohdr;
	var integer len, len_len;

	if (stream_len < hdr_len + 1) {
		return -1;
	}
	stream_nohdr := substr(stream, hdr_len, stream_len-hdr_len);

	len := f_aper_len_det(stream_nohdr, len_len);
	if (len < 0) {
		/* error: return to caller */
		return len;
	}
	if (len < 16384) {
		/* full length is known: return to caller */
		return hdr_len + len_len + len;
	} else {
		/* 'cursor' to next length indicator */
		var integer cur := hdr_len + len_len + len;
		/* iterate the whole chain of chunks */
		while (true) {
			if (stream_len < cur + 1) {
				return -1;
			}
			len := f_aper_len_det(substr(stream, cur, stream_len-cur), len_len);
			if (len < 0) {
				/* error: return to caller */
				return len;
			}
			if (len < 16384) {
				/* final chunk: segment with less than 16384 bytes */
				return cur + len_len + len;
			} else {
				/* point to next chunk */
				cur := cur + len_len + len;
			}
		}
	}
	/* not reached */
	return -2;
}

private function f_set_tcp_segmentation(integer idx) runs on SABP_Adapter_CT {
	/* Set function for dissecting the binary stream into packets */
	var f_IPL4_getMsgLen vl_f := refers(f_APER_getMsgLen);
	/* Offset: 1, size of length: 3, delta: 4, multiplier: 1, big-endian */
	SABP_CodecPort_CtrlFunct.f_IPL4_setGetMsgLen(SABP[idx], g_sabp_conn_id[idx], vl_f, {SABP_HDR_LEN});
}

function f_connect(charstring remote_host, IPL4asp_Types.PortNumber remote_port,
		   charstring local_host, IPL4asp_Types.PortNumber local_port, integer idx := 0)
runs on SABP_Adapter_CT {
	var IPL4asp_Types.Result res;
	map(self:SABP[idx], system:SABP);
	res := SABP_CodecPort_CtrlFunct.f_IPL4_connect(SABP[idx], remote_host, remote_port,
							local_host, local_port, 0, { tcp :={} });
	if (not ispresent(res.connId)) {
		setverdict(fail, "Could not connect to SABP port, check your configuration");
		mtc.stop;
	}
	g_sabp_conn_id[idx] := res.connId;

	f_set_tcp_segmentation(idx);
}

/* Function to use to bind to a local port as IPA server, accepting remote clients */
function f_bind(charstring local_host, IPL4asp_Types.PortNumber local_port, integer idx := 0)
runs on SABP_Adapter_CT {
	var IPL4asp_Types.Result res;
	map(self:SABP[idx], system:SABP);
	res := SABP_CodecPort_CtrlFunct.f_IPL4_listen(SABP[idx], local_host, local_port, { tcp:={} });
	g_sabp_conn_id[idx] := res.connId;

	f_set_tcp_segmentation(idx);
}

function f_sabp_send(template (value) SABP_PDU pdu, integer idx := 0) runs on SABP_Adapter_CT {
	SABP[idx].send(ts_SABP_Send(g_sabp_conn_id[idx], pdu));
}

function f_sabp_exp(template SABP_PDU exp, integer idx := 0) runs on SABP_Adapter_CT return SABP_PDU {
	var SABP_RecvFrom rf;
	SABP[idx].receive(tr_SABP_Recv(g_sabp_conn_id[idx], exp)) -> value rf;
	return rf.msg;
}


}