aboutsummaryrefslogtreecommitdiffstats
path: root/library/NS_Provider_IPL4.ttcn
blob: 09e7dc0e25a437bf8b95eb6b416576c107119540 (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
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
/* NS Provider for NS/UDP/IP
 * (C) 2020-2021 Harald Welte <laforge@gnumonks.org>
 * contributions by sysmocom - s.f.m.c. GmbH
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/* This provider can be operated in two modes:
 *
 * 1) the "classic" mode, where - similar to the NS_Provider_FR - there is
 *    only one NSVC per provider.  In this mode, the "NSE" port is used to
 *    exchange data with the next higher level component, such as a NSVC_CT
 *    or a RAW_NS_CT.
 *
 * 2) the new "endpoint" mode, where one provider can host a number of different
 *    NSVCs.  This is needed in most non-trivial IP-SNS scenarios.   The 'NSE'
 *    port of this component is no longer used.  Instead, there is a NSVC port
 *    array, one of which will be used for each NSVC. The NSVCs are dynamically
 *    added and removed via the PROC procedure port, controlled by NS_CT.
 */

module NS_Provider_IPL4 {

import from Misc_Helpers all;
import from NS_Emulation all;
import from RAW_NS all;
import from NS_Types all;

import from IPL4asp_Types all;
import from IPL4asp_PortType all;

/* maximum number of NS-VCs within one Provider (== IP endpoint) */
private const integer NUM_MAX_NSVC := 16;

type component NS_Provider_IPL4_CT extends NS_Provider_CT {
	/* down-facing port towards IPL4asp to IUT */
	port IPL4asp_PT IPL4;
	var integer g_conn_id := -1;

	/* per-NSVC ports and state */
	port NS_PROVIDER_PT NSVC[NUM_MAX_NSVC];
	var boolean g_nsvc_bound[NUM_MAX_NSVC];
	var PerNsvcState g_nsvc[NUM_MAX_NSVC];

	/* management port via which  */
	port NSPIP_PROC_PT PROC;
};

type record PerNsvcState {
	charstring remote_ip,
	PortNumber remote_port,
	NSVC_CT	vc_nsvc
};

signature NSPIP_add_nsvc(charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) return integer;
signature NSPIP_del_nsvc(charstring remote_ip, PortNumber remote_port) return integer;

type port NSPIP_PROC_PT procedure {
	inout NSPIP_add_nsvc, NSPIP_del_nsvc;
} with { extension "internal" };

/* add a new NSVC to the provider */
private function f_nsvc_add(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer
{
	for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) {
		if (g_nsvc_bound[i] == false) {
			g_nsvc[i] := nsvc;
			g_nsvc_bound[i] := true;
			if (isbound(nsvc.vc_nsvc) and nsvc.vc_nsvc != null) {
				connect(self:NSVC[i], nsvc.vc_nsvc:NSCP);
				NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP});
			}
			return i;
		}
	}
	Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Overflow of g_nsvc array"));
	return -1;
}

private function f_nsvc_del(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer
{
	for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) {
		if (g_nsvc_bound[i] and
		    g_nsvc[i].remote_ip == nsvc.remote_ip and
		    g_nsvc[i].remote_port == nsvc.remote_port) {
			g_nsvc[i] := {
				remote_ip := -,
				remote_port := -,
				vc_nsvc := null
			}
			g_nsvc_bound[i] := false;
			NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_DOWN});
			if (isbound(g_nsvc[i].vc_nsvc) and g_nsvc[i].vc_nsvc != null) {
				disconnect(self:NSVC[i], nsvc.vc_nsvc:NSCP);
			}
			return i;
		}
	}
	Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("attempt to delete unknown NSVC"));
	return -1;
}

private function f_get_nsvc_idx(charstring remote_ip, PortNumber remote_port)
runs on NS_Provider_IPL4_CT return integer
{
	for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) {
		if (g_nsvc_bound[i] and
		    g_nsvc[i].remote_ip == remote_ip and g_nsvc[i].remote_port == remote_port) {
			return i;
		}
	}
	return -1;
}

function main(NSVCConfiguration config, NSConfiguration nsconfig, charstring id) runs on NS_Provider_IPL4_CT {
	for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) {
		g_nsvc[i].vc_nsvc := null;
		g_nsvc_bound[i] := false;
	}

	/* in order to support any number of NSVC on this endpoiint, we only bind the socket
	 * to its local ip/port, but do not connect it to the remote peer provided in 'config'. */
	map(self:IPL4, system:IPL4);
	var Result res := f_IPL4_listen(IPL4, config.provider.ip.local_ip,
					 config.provider.ip.local_udp_port, { udp := {}});
	if (not ispresent(res.connId)) {
		setverdict(fail, "Could not connect NS UDP socket ", config.provider.ip);
		mtc.stop;
	}
	g_conn_id := res.connId;

	if (NSE.checkstate("Connected")) {
		NSE.send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP});
	}

	/* transceive between user-facing port and UDP socket */
	while (true) {
	var ASP_RecvFrom rx_rf;
	var PDU_NS rx_pdu;
	var integer rx_idx;
	var charstring remote_ip;
	var PortNumber remote_port;
	var NSVC_CT vc_nsvc;
	var NS_CT vc_caller;
	alt {

	[] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
		/* we have to resolve the NS-VC based on the remote peer */
		var integer nsvc_idx := f_get_nsvc_idx(rx_rf.remName, rx_rf.remPort);
		if (nsvc_idx == -1) {
			/* backwards compatibility; if there's no NSVC, send to NSE port */
			NSE.send(dec_PDU_NS(rx_rf.msg));
		} else {
			/* endpoint mode; send to the per-NSVC component via NSVC port */
			NSVC[nsvc_idx].send(dec_PDU_NS(rx_rf.msg));
		}
		}

	[] IPL4.receive(ASP_ConnId_ReadyToRelease:?) {
		}

	[] IPL4.receive(ASP_Event:?) {
		}

	[] any from NSVC.receive(PDU_NS:?) -> value rx_pdu @index value rx_idx {
		/* we can use the port array index directly into the g_nsvc array in order
		 * to resolve the IP + port of the remote peer to which to send */
		var ASP_SendTo tx := {
			connId := g_conn_id,
			remName := g_nsvc[rx_idx].remote_ip,
			remPort := g_nsvc[rx_idx].remote_port,
			proto := { udp := {} },
			msg := enc_PDU_NS(rx_pdu)
		};
		IPL4.send(tx);
		}
	[] NSE.receive(PDU_NS:?) -> value rx_pdu {
		/* backwards compatibility: If user uses the NSE port, use the destination
		 * provided during main() initialization */
		var ASP_SendTo tx := {
			connId := g_conn_id,
			remName := config.provider.ip.remote_ip,
			remPort := config.provider.ip.remote_udp_port,
			proto := { udp := {} },
			msg := enc_PDU_NS(rx_pdu)
		};
		IPL4.send(tx);
		}

	/* procedure port to add/remove NSVCs from this provider */
	[] PROC.getcall(NSPIP_add_nsvc:{?,?,?}) -> param (remote_ip, remote_port, vc_nsvc) sender vc_caller {
		var integer idx;
		idx := f_nsvc_add(PerNsvcState:{remote_ip, remote_port, vc_nsvc});
		PROC.reply(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc} value idx) to vc_caller;
		}
	[] PROC.getcall(NSPIP_del_nsvc:{?,?}) -> param (remote_ip, remote_port) sender vc_caller {
		var integer idx;
		idx := f_nsvc_del(PerNsvcState:{remote_ip, remote_port});
		PROC.reply(NSPIP_del_nsvc:{remote_ip, remote_port} value idx) to vc_caller;
		}

	} /* alt */
	} /* while */

} /* main */

function f_nspip_add_nsvc(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc)
runs on NS_CT {
	var integer idx := -1;
	NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc}) to vc_ipep {
		[] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx;
	}
}

function f_nspip_add_nsvc2(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port)
runs on RAW_NS_CT return integer {
	var integer idx := -1;
	NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, null}) to vc_ipep {
		[] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx;
	}

	return idx;
}

} /* module */