blob: 335a079a537b33cb689cd0ba25fa3f240d2752e3 [file] [log] [blame]
Alexander Couzens1c8785d2020-12-17 06:58:53 +01001/* test routines for NS connection handling
2 * (C) 2020 sysmocom - s.f.m.c. GmbH
3 * Author: Alexander Couzens <lynxis@fe80.eu>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
8#undef _GNU_SOURCE
9#define _GNU_SOURCE
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <unistd.h>
14#include <stdint.h>
15#include <string.h>
16#include <getopt.h>
17#include <dlfcn.h>
18#include <sys/types.h>
19#include <sys/socket.h>
20
21#include <osmocom/core/fsm.h>
22#include <osmocom/core/msgb.h>
Alexander Couzens47afc422021-01-17 20:12:46 +010023#include <osmocom/core/utils.h>
Alexander Couzens1c8785d2020-12-17 06:58:53 +010024#include <osmocom/core/application.h>
25#include <osmocom/core/utils.h>
26#include <osmocom/core/logging.h>
27#include <osmocom/core/socket.h>
28#include <osmocom/core/talloc.h>
29#include <osmocom/core/write_queue.h>
30#include <osmocom/gprs/gprs_msgb.h>
31#include <osmocom/gprs/gprs_ns2.h>
32#include <osmocom/gprs/gprs_bssgp.h>
33
34#include "../../src/gb/gprs_ns2_internal.h"
35
36int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
37{
38 return -1;
39}
40
41static struct log_info info = {};
Alexander Couzens8f215c92021-01-25 15:27:56 +010042static struct osmo_wqueue *unitdata = NULL;
Alexander Couzens1c8785d2020-12-17 06:58:53 +010043
44static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
45{
Alexander Couzens8f215c92021-01-25 15:27:56 +010046 OSMO_ASSERT(oph->sap == SAP_NS);
47 if (oph->msg) {
48 if (oph->primitive == PRIM_NS_UNIT_DATA) {
49 osmo_wqueue_enqueue(unitdata, oph->msg);
50 } else {
51 msgb_free(oph->msg);
52 }
53 }
Alexander Couzens1c8785d2020-12-17 06:58:53 +010054 return 0;
55}
56
Alexander Couzens47afc422021-01-17 20:12:46 +010057static struct msgb *get_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
58{
59 struct gprs_ns_hdr *nsh;
60 struct osmo_wqueue *queue = bind->priv;
61
62 while (!llist_empty(&queue->msg_queue)) {
63 struct msgb *msg = msgb_dequeue(&queue->msg_queue);
64 nsh = (struct gprs_ns_hdr *) msg->l2h;
65 if (nsh->pdu_type == pdu_type)
66 return msg;
67 msgb_free(msg);
68 }
69
70 return NULL;
71}
72
73static bool find_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
74{
75 struct msgb *msg;
76 msg = get_pdu(bind, pdu_type);
77 if (msg) {
78 msgb_free(msg);
79 return true;
80 }
81
82 return false;
83}
84
Alexander Couzensaf5bfeb2021-01-17 20:12:37 +010085static void clear_pdus(struct gprs_ns2_vc_bind *bind)
Alexander Couzens1c8785d2020-12-17 06:58:53 +010086{
Alexander Couzensaf5bfeb2021-01-17 20:12:37 +010087 struct osmo_wqueue *queue = bind->priv;
88 osmo_wqueue_clear(queue);
Alexander Couzens1c8785d2020-12-17 06:58:53 +010089}
90
91struct gprs_ns2_vc_driver vc_driver_dummy = {
92 .name = "GB UDP dummy",
Alexander Couzensaf5bfeb2021-01-17 20:12:37 +010093 .free_bind = clear_pdus,
Alexander Couzens1c8785d2020-12-17 06:58:53 +010094};
95
96static int vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
97{
98 struct gprs_ns2_vc_bind *bind = nsvc->bind;
99 struct osmo_wqueue *queue = bind->priv;
100
101 osmo_wqueue_enqueue(queue, msg);
102 return 0;
103}
104
105static struct gprs_ns2_vc_bind *dummy_bind(struct gprs_ns2_inst *nsi, const char *name)
106{
107 struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
108 OSMO_ASSERT(bind);
109
110 bind->name = talloc_strdup(bind, name);
111 bind->driver = &vc_driver_dummy;
112 bind->ll = GPRS_NS2_LL_UDP;
113 bind->transfer_capability = 42;
114 bind->nsi = nsi;
115 bind->send_vc = vc_sendmsg;
116 bind->priv = talloc_zero(bind, struct osmo_wqueue);
117 struct osmo_wqueue *queue = bind->priv;
118
119 INIT_LLIST_HEAD(&bind->nsvc);
120 llist_add(&bind->list, &nsi->binding);
121 osmo_wqueue_init(queue, 100);
122
123 return bind;
124}
125
Alexander Couzens8f215c92021-01-25 15:27:56 +0100126static void free_loopback(struct gprs_ns2_vc_bind *bind) {}
127
128struct gprs_ns2_vc_driver vc_driver_loopback = {
129 .name = "loopback dummy",
130 .free_bind = free_loopback,
131};
132
133/* loopback the msg */
134static int loopback_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
135{
136 struct gprs_ns2_vc *target = nsvc->priv;
137 return ns2_recv_vc(target, msg);
138}
139
140/* create a loopback nsvc object which can be used with ns2_tx_* functions. it's not fully registered etc. */
141static struct gprs_ns2_vc *loopback_nsvc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *target)
142{
143 struct gprs_ns2_vc *nsvc = talloc_zero(bind, struct gprs_ns2_vc);
144 memcpy(nsvc, target, sizeof(struct gprs_ns2_vc));
145 nsvc->bind = bind;
146 nsvc->priv = target;
147 return nsvc;
148}
149
150/* a loop back bind to use the tx_ functions from gprs_ns2_message.c */
151static struct gprs_ns2_vc_bind *loopback_bind(struct gprs_ns2_inst *nsi, const char *name)
152{
153 struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
154 OSMO_ASSERT(bind);
155 bind->name = talloc_strdup(bind, name);
156 bind->driver = &vc_driver_loopback;
157 bind->ll = GPRS_NS2_LL_UDP;
158 bind->transfer_capability = 99;
159 bind->nsi = nsi;
160 bind->send_vc = loopback_sendmsg;
161 INIT_LLIST_HEAD(&bind->nsvc);
162 llist_add(&bind->list, &nsi->binding);
163 return bind;
164}
165
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100166void test_nse_transfer_cap(void *ctx)
167{
168 struct gprs_ns2_inst *nsi;
169 struct gprs_ns2_vc_bind *bind[2];
170 struct gprs_ns2_nse *nse;
171 struct gprs_ns2_vc *nsvc[3];
172
173 /* create a UDP dummy bind[0] with transfer cap 42.
174 * create nse (nsei 1001)
175 * create 2x nsvc with the same bind.
176 * nsvc[0] or nsvc[1] is alive (or both) cap == 42
177 *
178 * create a second bind with transfer cap == 23
179 * create 3rd nsvc with bind[1]
180 * transfer cap should be 42 + 23
181 */
182
183 printf("--- Testing NSE transfer cap\n");
184
185 printf("---- Create NSE + Binds\n");
186 nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
187 bind[0] = dummy_bind(nsi, "transfercap1");
188 bind[1] = dummy_bind(nsi, "transfercap2");
189 bind[1]->transfer_capability = 23;
190 nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, NS2_DIALECT_STATIC_ALIVE);
191 OSMO_ASSERT(nse);
192
193 printf("---- Test with NSVC[0]\n");
Harald Welte603f4042020-11-29 17:39:19 +0100194 nsvc[0] = ns2_vc_alloc(bind[0], nse, false, NS2_VC_MODE_ALIVE, NULL);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100195 OSMO_ASSERT(nsvc[0]);
196 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 0);
197 nsvc[0]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
198 ns2_nse_notify_unblocked(nsvc[0], true);
199 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
200
201 printf("---- Test with NSVC[1]\n");
Harald Welte603f4042020-11-29 17:39:19 +0100202 nsvc[1] = ns2_vc_alloc(bind[0], nse, false, NS2_VC_MODE_ALIVE, NULL);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100203 OSMO_ASSERT(nsvc[1]);
204 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
205 nsvc[1]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
206 ns2_nse_notify_unblocked(nsvc[1], true);
207 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
208
209 printf("---- Test with NSVC[2]\n");
Harald Welte603f4042020-11-29 17:39:19 +0100210 nsvc[2] = ns2_vc_alloc(bind[1], nse, false, NS2_VC_MODE_ALIVE, NULL);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100211 OSMO_ASSERT(nsvc[2]);
212 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
213 nsvc[2]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
214 ns2_nse_notify_unblocked(nsvc[2], true);
215 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42 + 23);
216
217 printf("---- Test with NSVC[1] removed\n");
218 /* reset nsvc[1] to be unconfigured - shouldn't change anything */
219 nsvc[1]->fi->state = 0; /* HACK: 0 = GPRS_NS2_ST_UNCONFIGURED */
220 ns2_nse_notify_unblocked(nsvc[1], false);
221 OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42 + 23);
222
Alexander Couzensaca31b82021-01-17 20:15:20 +0100223 gprs_ns2_free(nsi);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100224 printf("--- Finish NSE transfer cap\n");
225
226}
227
Alexander Couzens47afc422021-01-17 20:12:46 +0100228/* setup NSE with 2x NSVCs.
229 * block 1x NSVC
230 * unblock 1x NSVC*/
231void test_block_unblock_nsvc(void *ctx)
232{
233 struct gprs_ns2_inst *nsi;
234 struct gprs_ns2_vc_bind *bind[2];
235 struct gprs_ns2_nse *nse;
236 struct gprs_ns2_vc *nsvc[2];
237 struct gprs_ns_hdr *nsh;
238 struct msgb *msg;
239 char idbuf[32];
Harald Welte2846ee22021-01-21 10:22:09 +0100240 int i;
Alexander Couzens47afc422021-01-17 20:12:46 +0100241
242 printf("--- Testing NSE block unblock nsvc\n");
243 printf("---- Create NSE + Binds\n");
244 nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
245 bind[0] = dummy_bind(nsi, "bblock1");
246 bind[1] = dummy_bind(nsi, "bblock2");
247 nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, NS2_DIALECT_STATIC_RESETBLOCK);
248 OSMO_ASSERT(nse);
249
Harald Welte2846ee22021-01-21 10:22:09 +0100250 for (i=0; i<2; i++) {
Alexander Couzens47afc422021-01-17 20:12:46 +0100251 printf("---- Create NSVC[i]\n");
252 snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
253 nsvc[i] = ns2_vc_alloc(bind[i], nse, false, NS2_VC_MODE_BLOCKRESET, idbuf);
254 OSMO_ASSERT(nsvc[i]);
255 nsvc[i]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
256 /* ensure the fi->state works correct */
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100257 OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
Alexander Couzens47afc422021-01-17 20:12:46 +0100258 ns2_nse_notify_unblocked(nsvc[i], true);
259 }
260
261 /* both nsvcs are unblocked and alive. Let's block it. */
262 OSMO_ASSERT(!find_pdu(bind[0], NS_PDUT_BLOCK));
263 clear_pdus(bind[0]);
264 ns2_vc_block(nsvc[0]);
265 OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_BLOCK));
266 /* state == BLOCKED */
267 clear_pdus(bind[0]);
268
269 /* now unblocking it */
270 ns2_vc_unblock(nsvc[0]);
271 OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_UNBLOCK));
272 clear_pdus(bind[0]);
273
274 msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "test_unblock");
275 msg->l2h = msgb_put(msg, sizeof(*nsh));
276 nsh = (struct gprs_ns_hdr *) msg->l2h;
277 nsh->pdu_type = NS_PDUT_UNBLOCK_ACK;
278 ns2_recv_vc(nsvc[0], msg);
279
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100280 OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[0]));
Alexander Couzens47afc422021-01-17 20:12:46 +0100281 gprs_ns2_free(nsi);
282 printf("--- Finish NSE block unblock nsvc\n");
283}
284
Alexander Couzens8f215c92021-01-25 15:27:56 +0100285static struct msgb *generate_unitdata(const char *msgname)
286{
287 struct gprs_ns_hdr *nsh;
288 struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, msgname);
289 OSMO_ASSERT(msg);
290
291 msg->l2h = msgb_put(msg, sizeof(*nsh) + 6);
292 nsh = (struct gprs_ns_hdr *) msg->l2h;
293 nsh->pdu_type = NS_PDUT_UNITDATA;
294 nsh->data[0] = 0; /* sdu control */
295 nsh->data[1] = 0; /* msb bvci */
296 nsh->data[2] = 12; /* lsb bvci */
297 nsh->data[3] = 0xab; /* first data byte */
298 nsh->data[4] = 0xcd;
299 nsh->data[5] = 0xef;
300
301 return msg;
302}
303
304void test_unitdata(void *ctx)
305{
306 struct gprs_ns2_inst *nsi;
307 struct gprs_ns2_vc_bind *bind[2];
308 struct gprs_ns2_vc_bind *loopbind;
309 struct gprs_ns2_nse *nse;
310 struct gprs_ns2_vc *nsvc[2];
311 struct gprs_ns2_vc *loop[2];
312
313 struct msgb *msg, *other;
314 char idbuf[32];
315 int i;
316
317 printf("--- Testing unitdata test\n");
318 osmo_wqueue_clear(unitdata);
319 printf("---- Create NSE + Binds\n");
320 nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
321 bind[0] = dummy_bind(nsi, "bblock1");
322 bind[1] = dummy_bind(nsi, "bblock2");
323 loopbind = loopback_bind(nsi, "loopback");
324 nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, NS2_DIALECT_STATIC_RESETBLOCK);
325 OSMO_ASSERT(nse);
326
327 for (i=0; i<2; i++) {
328 printf("---- Create NSVC[%d]\n", i);
329 snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
330 nsvc[i] = ns2_vc_alloc(bind[i], nse, false, NS2_VC_MODE_BLOCKRESET, idbuf);
331 loop[i] = loopback_nsvc(loopbind, nsvc[i]);
332 OSMO_ASSERT(nsvc[i]);
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100333 ns2_vc_fsm_start(nsvc[i]);
334 OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
Alexander Couzens8f215c92021-01-25 15:27:56 +0100335 ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
336 ns2_tx_unblock(loop[i]);
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100337 OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
Alexander Couzens8f215c92021-01-25 15:27:56 +0100338 }
339
340 /* both nsvcs are unblocked and alive */
341 printf("---- Send UNITDATA to NSVC[0]\n");
342 msg = generate_unitdata("test_unitdata");
343 ns2_recv_vc(nsvc[0], msg);
344 other = msgb_dequeue(&unitdata->msg_queue);
345 OSMO_ASSERT(msg == other);
346 other = msgb_dequeue(&unitdata->msg_queue);
347 OSMO_ASSERT(NULL == other);
348
349 printf("---- Send Block NSVC[0]\n");
350 ns2_vc_block(nsvc[0]);
351 ns2_tx_block_ack(loop[0]);
352
353 /* try to receive a unitdata - this should be dropped & freed by NS */
354 printf("---- Try to receive over blocked NSVC[0]\n");
355 ns2_recv_vc(nsvc[0], msg);
356 other = msgb_dequeue(&unitdata->msg_queue);
357 OSMO_ASSERT(NULL == other);
358
359 /* nsvc[1] should be still good */
360 printf("---- Receive over NSVC[1]\n");
361 msg = generate_unitdata("test_unitdata2");
362 ns2_recv_vc(nsvc[1], msg);
363 other = msgb_dequeue(&unitdata->msg_queue);
364 OSMO_ASSERT(msg == other);
365 msgb_free(msg);
366
367 gprs_ns2_free(nsi);
368 printf("--- Finish unitdata test\n");
369}
370
371
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100372int main(int argc, char **argv)
373{
374 void *ctx = talloc_named_const(NULL, 0, "gprs_ns2_test");
375 osmo_init_logging2(ctx, &info);
376 log_set_use_color(osmo_stderr_target, 0);
377 log_set_print_filename(osmo_stderr_target, 0);
378 log_set_print_filename(osmo_stderr_target, 0);
379 log_set_log_level(osmo_stderr_target, LOGL_INFO);
Alexander Couzens8f215c92021-01-25 15:27:56 +0100380 unitdata = talloc_zero(ctx, struct osmo_wqueue);
381 osmo_wqueue_init(unitdata, 100);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100382 setlinebuf(stdout);
383
384 printf("===== NS2 protocol test START\n");
385 test_nse_transfer_cap(ctx);
Alexander Couzens47afc422021-01-17 20:12:46 +0100386 test_block_unblock_nsvc(ctx);
Alexander Couzens8f215c92021-01-25 15:27:56 +0100387 test_unitdata(ctx);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100388 printf("===== NS2 protocol test END\n\n");
389
Alexander Couzense19b7962021-01-17 20:07:54 +0100390 talloc_free(ctx);
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100391 exit(EXIT_SUCCESS);
392}