blob: 0b3aeb54f8f826500a151a5a56936c364f493bb1 [file] [log] [blame]
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +02001/* Test the GTP hub */
2
3/* (C) 2015 by sysmocom s.f.m.c. GmbH
4 * All Rights Reserved
5 *
6 * Author: Neels Hofmeyr <nhofmeyr@sysmcom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <limits.h>
26#include <unistd.h>
27
28#include <osmocom/core/utils.h>
29#include <osmocom/core/msgb.h>
30#include <osmocom/core/application.h>
31
32#include <openbsc/debug.h>
33
34#include <openbsc/gtphub.h>
35#include <gtp.h>
36#include <gtpie.h>
37
38#define EXPIRE_ALL ((60 * GTPH_TEI_MAPPING_EXPIRY_MINUTES) + 1)
39
Neels Hofmeyrc2275942015-11-10 22:07:04 +010040#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', sizeof(*(struct_pointer)))
41
42#define LVL2_ASSERT(exp) LVL2_ASSERT_R(exp, return 0)
43#define LVL2_ASSERT_R(exp, ret) \
44 if (!(exp)) { \
45 fprintf(stderr, "LVL2 Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \
46 osmo_generate_backtrace(); \
47 ret; \
48 }
49
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020050void gtphub_init(struct gtphub *hub);
51
52void *osmo_gtphub_ctx;
53
54/* TODO copied from libosmo-abis/src/subchan_demux.c, remove dup */
55static int llist_len(struct llist_head *head)
56{
57 struct llist_head *entry;
58 int i = 0;
59
60 llist_for_each(entry, head)
61 i++;
62
63 return i;
64}
65
66static void nr_mapping_free(struct expiring_item *e)
67{
68 struct nr_mapping *m = container_of(e, struct nr_mapping,
69 expiry_entry);
70 nr_mapping_del(m);
71 talloc_free(m);
72}
73
74static struct nr_mapping *nr_mapping_alloc(void)
75{
76 struct nr_mapping *m;
77 m = talloc(osmo_gtphub_ctx, struct nr_mapping);
78 nr_mapping_init(m);
79 m->expiry_entry.del_cb = nr_mapping_free;
80 return m;
81}
82
83static struct nr_mapping *nr_map_have(struct nr_map *map, void *origin, nr_t orig, time_t now)
84{
85 struct nr_mapping *mapping;
86
87 mapping = nr_map_get(map, origin, orig);
88 if (!mapping) {
89 mapping = nr_mapping_alloc();
90 mapping->origin = origin;
91 mapping->orig = orig;
92 nr_map_add(map, mapping, now);
93 }
94
95 return mapping;
96}
97
98static nr_t nr_map_verify(const struct nr_map *map, void *origin, nr_t orig, nr_t expect_repl)
99{
100 struct nr_mapping *m;
101 m = nr_map_get(map, origin, orig);
102
103 if (!m) {
104 printf("mapping not found for %p %d\n", origin, orig);
105 return 0;
106 }
107
108 if (m->repl != expect_repl) {
109 printf("mapping found, but nr mismatches: expect %d, got %d\n",
110 (int)expect_repl, (int)m->repl);
111 return 0;
112 }
113
114 return 1;
115}
116
117static int nr_map_verify_inv(const struct nr_map *map, nr_t repl,
118 void *expect_origin, nr_t expect_orig)
119{
120 struct nr_mapping *m;
121 m = nr_map_get_inv(map, repl);
122 if (!m) {
123 printf("mapping not found for %d\n", (int)repl);
124 return 0;
125 }
126
127 if (m->origin != expect_origin) {
128 printf("mapping found, but origin mismatches: expect %p, got %p\n",
129 expect_origin, m->origin);
130 return 0;
131 }
132
133 if (m->orig != expect_orig) {
134 printf("mapping found, but nr mismatches: expect %d, got %d\n",
135 (int)expect_orig, (int)m->orig);
136 return 0;
137 }
138
139 return 1;
140}
141
142
143static void test_nr_map_basic(void)
144{
145 struct nr_pool _pool;
146 struct nr_pool *pool = &_pool;
147 struct nr_map _map;
148 struct nr_map *map = &_map;
149
150 nr_pool_init(pool);
151 nr_map_init(map, pool, NULL);
152
153 OSMO_ASSERT(llist_empty(&map->mappings));
154
155#define TEST_N_HALF 100
156#define TEST_N (2*TEST_N_HALF)
157#define TEST_I 123
158 uint32_t i, check_i;
159 uint32_t m[TEST_N];
160 struct nr_mapping *mapping;
161
162 /* create half of TEST_N mappings from one origin */
163 void *origin1 = (void*)0x1234;
164 for (i = 0; i < TEST_N_HALF; i++) {
165 nr_t orig = TEST_I + i;
166 mapping = nr_map_have(map, origin1, orig, 0);
167 m[i] = mapping->repl;
168 OSMO_ASSERT(m[i] != 0);
169 OSMO_ASSERT(llist_len(&map->mappings) == (i+1));
170 for (check_i = 0; check_i < i; check_i++)
171 OSMO_ASSERT(m[check_i] != m[i]);
172 }
173 OSMO_ASSERT(llist_len(&map->mappings) == TEST_N_HALF);
174
175 /* create another TEST_N mappings with the same original numbers, but
176 * from a different origin */
177 void *origin2 = (void*)0x5678;
178 for (i = 0; i < TEST_N_HALF; i++) {
179 int i2 = TEST_N_HALF + i;
180 nr_t orig = TEST_I + i;
181 mapping = nr_map_have(map, origin2, orig, 0);
182 m[i2] = mapping->repl;
183 OSMO_ASSERT(m[i2] != 0);
184 OSMO_ASSERT(llist_len(&map->mappings) == (i2+1));
185 for (check_i = 0; check_i < i2; check_i++)
186 OSMO_ASSERT(m[check_i] != m[i2]);
187 }
188 OSMO_ASSERT(llist_len(&map->mappings) == TEST_N);
189
190 /* verify mappings */
191 for (i = 0; i < TEST_N_HALF; i++) {
192 nr_t orig = TEST_I + i;
193 {
194 OSMO_ASSERT(nr_map_verify(map, origin1, orig, m[i]));
195 OSMO_ASSERT(nr_map_verify_inv(map, m[i], origin1, orig));
196 }
197 {
198 int i2 = TEST_N_HALF + i;
199 OSMO_ASSERT(nr_map_verify(map, origin2, orig, m[i2]));
200 OSMO_ASSERT(nr_map_verify_inv(map, m[i2], origin2, orig));
201 }
202 }
203
204 /* remove all mappings */
205 for (i = 0; i < TEST_N_HALF; i++) {
206 OSMO_ASSERT(llist_len(&map->mappings) == (TEST_N - 2*i));
207
208 nr_t orig = TEST_I + i;
209 nr_mapping_del(nr_map_get(map, origin1, orig));
210 nr_mapping_del(nr_map_get(map, origin2, orig));
211 }
212 OSMO_ASSERT(llist_empty(&map->mappings));
213#undef TEST_N
214#undef TEST_I
215}
216
217static int nr_map_is(struct nr_map *map, const char *str)
218{
219 static char buf[4096];
220 char *pos = buf;
221 size_t len = sizeof(buf);
222 struct nr_mapping *m;
223 llist_for_each_entry(m, &map->mappings, entry) {
224 size_t wrote = snprintf(pos, len, "(%d->%d@%d), ",
225 (int)m->orig,
226 (int)m->repl,
227 (int)m->expiry_entry.expiry);
228 OSMO_ASSERT(wrote < len);
229 pos += wrote;
230 len -= wrote;
231 }
232 *pos = '\0';
233
234 if (strncmp(buf, str, sizeof(buf)) != 0) {
235 printf("FAILURE: nr_map_is() mismatches expected value:\n"
236 "expected: \"%s\"\n"
237 "is: \"%s\"\n",
238 str, buf);
239 return 0;
240 }
241 return 1;
242}
243
244static void test_expiry(void)
245{
246 struct expiry expiry;
247 struct nr_pool pool;
248 struct nr_map map;
249 int i;
250
251 expiry_init(&expiry, 30);
252 nr_pool_init(&pool);
253 nr_map_init(&map, &pool, &expiry);
254 OSMO_ASSERT(nr_map_is(&map, ""));
255
256 /* tick on empty map */
257 OSMO_ASSERT(expiry_tick(&expiry, 10000) == 0);
258 OSMO_ASSERT(nr_map_is(&map, ""));
259
260#define MAP1 \
261 "(10->1@10040), " \
262 ""
263
264#define MAP2 \
265 "(20->2@10050), " \
266 "(21->3@10051), " \
267 "(22->4@10052), " \
268 "(23->5@10053), " \
269 "(24->6@10054), " \
270 "(25->7@10055), " \
271 "(26->8@10056), " \
272 "(27->9@10057), " \
273 ""
274
275#define MAP3 \
276 "(420->10@10072), " \
277 "(421->11@10072), " \
278 "(422->12@10072), " \
279 "(423->13@10072), " \
280 "(424->14@10072), " \
281 "(425->15@10072), " \
282 "(426->16@10072), " \
283 "(427->17@10072), " \
284 ""
285
286 /* add mapping at time 10010. */
287 nr_map_have(&map, 0, 10, 10010);
288 OSMO_ASSERT(nr_map_is(&map, MAP1));
289
290 /* tick on unexpired item. */
291 OSMO_ASSERT(expiry_tick(&expiry, 10010) == 0);
292 OSMO_ASSERT(expiry_tick(&expiry, 10011) == 0);
293 OSMO_ASSERT(nr_map_is(&map, MAP1));
294
295 /* Spread mappings at 10020, 10021, ... 10027. */
296 for (i = 0; i < 8; i++)
297 nr_map_have(&map, 0, 20 + i, 10020 + i);
298 OSMO_ASSERT(nr_map_is(&map, MAP1 MAP2));
299
300 /* tick on unexpired items. */
301 OSMO_ASSERT(expiry_tick(&expiry, 10030) == 0);
302 OSMO_ASSERT(expiry_tick(&expiry, 10039) == 0);
303 OSMO_ASSERT(nr_map_is(&map, MAP1 MAP2));
304
305 /* expire the first item (from 10010). */
306 OSMO_ASSERT(expiry_tick(&expiry, 10010 + 30) == 1);
307 OSMO_ASSERT(nr_map_is(&map, MAP2));
308
309 /* again nothing to expire */
310 OSMO_ASSERT(expiry_tick(&expiry, 10041) == 0);
311 OSMO_ASSERT(nr_map_is(&map, MAP2));
312
313 /* Mappings all at the same time. */
314 for (i = 0; i < 8; i++)
315 nr_map_have(&map, 0, 420 + i, 10042);
316 OSMO_ASSERT(nr_map_is(&map, MAP2 MAP3));
317
318 /* Eight to expire, were added further above to be chronologically
319 * correct, at 10020..10027. */
320 OSMO_ASSERT(expiry_tick(&expiry, 10027 + 30) == 8);
321 OSMO_ASSERT(nr_map_is(&map, MAP3));
322
323 /* again nothing to expire */
324 OSMO_ASSERT(expiry_tick(&expiry, 10027 + 30) == 0);
325 OSMO_ASSERT(nr_map_is(&map, MAP3));
326
327 /* Eight to expire, from 10042. Now at 10042 + 30: */
328 OSMO_ASSERT(expiry_tick(&expiry, 10042 + 30) == 8);
329 OSMO_ASSERT(nr_map_is(&map, ""));
330
331#undef MAP1
332#undef MAP2
333#undef MAP3
334}
335
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100336char resolve_ggsn_got_imsi[GSM_IMSI_LENGTH];
337char resolve_ggsn_got_ni[GSM_APN_LENGTH];
338
339struct osmo_sockaddr resolved_ggsn_addr;
340static int resolve_to_ggsn(const char *addr, uint16_t port)
341{
342 LVL2_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_addr,
343 addr, port)
344 == 0);
345 return 1;
346}
347
348struct osmo_sockaddr sgsn_sender;
349static int send_from_sgsn(const char *addr, uint16_t port)
350{
351 LVL2_ASSERT(osmo_sockaddr_init_udp(&sgsn_sender,
352 addr, port)
353 == 0);
354 return 1;
355}
356
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200357
358
359/* override, requires '-Wl,--wrap=gtphub_resolve_ggsn_addr' */
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100360struct gtphub_peer_port *__real_gtphub_resolve_ggsn_addr(struct gtphub *hub,
361 const char *imsi_str,
362 const char *apn_ni_str);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200363
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100364struct gtphub_peer_port *__wrap_gtphub_resolve_ggsn_addr(struct gtphub *hub,
365 const char *imsi_str,
366 const char *apn_ni_str)
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200367{
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100368 struct gsn_addr resolved_gsna;
369 uint16_t resolved_port;
370
371 OSMO_ASSERT(gsn_addr_from_sockaddr(&resolved_gsna, &resolved_port,
372 &resolved_ggsn_addr) == 0);
373
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100374 struct gtphub_peer_port *pp;
375 pp = gtphub_port_have(hub, &hub->to_ggsns[GTPH_PLANE_CTRL],
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100376 &resolved_gsna, resolved_port);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100377 printf("Wrap: returning GGSN addr from imsi %s ni %s: %s\n",
378 imsi_str, apn_ni_str, gtphub_port_str(pp));
379
380 if (imsi_str) {
381 strncpy(resolve_ggsn_got_imsi, imsi_str, sizeof(resolve_ggsn_got_imsi));
382 resolve_ggsn_got_imsi[sizeof(resolve_ggsn_got_imsi) - 1] = '\0';
383 }
384 else
385 strcpy(resolve_ggsn_got_imsi, "(null)");
386
387 if (apn_ni_str) {
388 strncpy(resolve_ggsn_got_ni, apn_ni_str, sizeof(resolve_ggsn_got_ni));
389 resolve_ggsn_got_ni[sizeof(resolve_ggsn_got_ni) - 1] = '\0';
390 }
391 else
392 strcpy(resolve_ggsn_got_ni, "(null)");
393
394 return pp;
395}
396
397#define was_resolved_for(IMSI,NI) _was_resolved_for(IMSI, NI, __FILE__, __LINE__)
398static int _was_resolved_for(const char *imsi, const char *ni, const char *file, int line)
399{
400 int cmp0 = strncmp(imsi, resolve_ggsn_got_imsi, sizeof(resolve_ggsn_got_imsi));
401
402 if (cmp0 != 0) {
403 printf("\n%s:%d: was_resolved_for(): MISMATCH for IMSI\n"
404 " expecting: '%s'\n"
405 " got: '%s'\n\n",
406 file,
407 line,
408 imsi, resolve_ggsn_got_imsi);
409 }
410
411 int cmp1 = strncmp(ni, resolve_ggsn_got_ni, sizeof(resolve_ggsn_got_ni));
412 if (cmp1 != 0) {
413 printf("\n%s:%d: was_resolved_for(): MISMATCH for NI\n"
414 " expecting: '%s'\n"
415 " got: '%s'\n\n",
416 file,
417 line,
418 ni, resolve_ggsn_got_ni);
419 }
420
421 return (cmp0 == 0) && (cmp1 == 0);
422}
423
424/* override, requires '-Wl,--wrap=gtphub_ares_init' */
425int __real_gtphub_ares_init(struct gtphub *hub);
426
427int __wrap_gtphub_ares_init(struct gtphub *hub)
428{
429 /* Do nothing. */
430 return 0;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200431}
432
433#define buf_len 1024
434static uint8_t buf[buf_len];
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100435static uint8_t *reply_buf;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200436
437static unsigned int msg(const char *hex)
438{
439 unsigned int l = osmo_hexparse(hex, buf, buf_len);
440 OSMO_ASSERT(l > 0);
441 return l;
442}
443
444/* Compare static buf to given string constant. The amount of bytes is obtained
445 * from parsing the GTP header in buf. hex must match an osmo_hexdump() of the
446 * desired message. Return 1 if size and content match. */
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100447#define reply_is(MSG) _reply_is(MSG, __FILE__, __LINE__)
448static int _reply_is(const char *hex, const char *file, int line)
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200449{
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100450 struct gtp1_header_long *h = (void*)reply_buf;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200451 int len = ntoh16(h->length) + 8;
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100452 const char *dump = osmo_hexdump_nospc(reply_buf, len);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200453 int cmp = strcmp(dump, hex);
454
455 if (cmp != 0) {
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100456 printf("\n%s:%d: reply_is(): MISMATCH\n"
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200457 " expecting:\n'%s'\n"
458 " got:\n'%s'\n\n",
459 file,
460 line,
461 hex, dump);
462 int i;
463 int l = strlen(hex);
464 int m = strlen(dump);
465 if (m < l)
466 l = m;
467 for (i = 0; i < l; i++) {
468 if (hex[i] != dump[i]) {
469 printf("First mismatch at position %d:\n"
470 " %s\n %s\n", i, hex + i, dump + i);
471 break;
472 }
473 }
474 }
475 return cmp == 0;
476}
477
478#define same_addr(GOT, EXPECTED) _same_addr((GOT),(EXPECTED), __FILE__, __LINE__)
479static int _same_addr(const struct osmo_sockaddr *got,
480 const struct osmo_sockaddr *expected,
481 const char *file, int line)
482{
483 int cmp = osmo_sockaddr_cmp(got, expected);
484 if (!cmp)
485 return 1;
486 char buf[256];
487 printf("\n%s:%d: addr_is(): MISMATCH\n"
488 " expecting: '%s'\n"
489 " got: '%s'\n\n",
490 file, line,
491 osmo_sockaddr_to_str(expected),
492 osmo_sockaddr_to_strb(got, buf, sizeof(buf)));
493 return 0;
494}
495
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100496
497time_t now;
498static struct gtphub _hub;
499static struct gtphub *hub = &_hub;
500
501static int setup_test_hub()
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200502{
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100503 /* Not really needed, but to make 100% sure... */
504 ZERO_STRUCT(hub);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200505
506 gtphub_init(hub);
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100507
508 /* Tell this mock gtphub its local address for this test. */
509 LVL2_ASSERT(gsn_addr_from_str(&hub->to_sgsns[GTPH_PLANE_CTRL].local_addr,
510 "127.0.1.1") == 0);
511 LVL2_ASSERT(gsn_addr_from_str(&hub->to_sgsns[GTPH_PLANE_USER].local_addr,
512 "127.0.1.2") == 0);
513 LVL2_ASSERT(gsn_addr_from_str(&hub->to_ggsns[GTPH_PLANE_CTRL].local_addr,
514 "127.0.2.1") == 0);
515 LVL2_ASSERT(gsn_addr_from_str(&hub->to_ggsns[GTPH_PLANE_USER].local_addr,
516 "127.0.2.2") == 0);
517
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100518 hub->restart_counter = 0x23;
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100519 now = 345;
520 LVL2_ASSERT(send_from_sgsn("192.168.42.23", 423));
521 LVL2_ASSERT(resolve_to_ggsn("192.168.43.34", 434));
522
523 return 1;
524}
525
526
527static void test_echo(void)
528{
529 OSMO_ASSERT(setup_test_hub());
530
531 now = 123;
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100532
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200533 const char *gtp_ping_from_sgsn =
534 "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */
535 "01" /* type 01: Echo request */
536 "0004" /* length of 4 after header TEI */
537 "00000000" /* header TEI == 0 in Echo */
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100538 "abcd" /* some 2 octet sequence nr */
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200539 "0000" /* N-PDU 0, no extension header (why is this here?) */
540 ;
541
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100542 const char *gtp_pong_to_sgsn =
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200543 "32"
544 "02" /* type 02: Echo response */
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100545 "0006" /* length of 6 after header TEI */
546 "00000000" /* header TEI == 0 in Echo */
547 "abcd" /* same sequence nr */
548 "0000"
549 "0e23" /* Recovery with restart counter */
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200550 ;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200551
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200552 struct osmo_fd *ggsn_ofd = NULL;
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100553 struct osmo_sockaddr to_addr;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200554 int send;
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100555 send = gtphub_from_sgsns_handle_buf(hub, GTPH_PLANE_CTRL, &sgsn_sender,
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200556 buf, msg(gtp_ping_from_sgsn), now,
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100557 &reply_buf, &ggsn_ofd, &to_addr);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200558 OSMO_ASSERT(send > 0);
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100559 OSMO_ASSERT(to_addr.l);
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100560 OSMO_ASSERT(same_addr(&to_addr, &sgsn_sender));
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100561 OSMO_ASSERT(reply_is(gtp_pong_to_sgsn));
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200562
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100563 struct gtphub_peer_port *sgsn_port =
564 gtphub_port_find_sa(&hub->to_sgsns[GTPH_PLANE_CTRL],
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100565 &sgsn_sender);
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100566 /* We don't record Echo peers. */
567 OSMO_ASSERT(!sgsn_port);
568
569 const char *gtp_ping_from_ggsn =
570 "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */
571 "01" /* type 01: Echo request */
572 "0004" /* length of 4 after header TEI */
573 "00000000" /* header TEI == 0 in Echo */
574 "cdef" /* some 2 octet sequence nr */
575 "0000" /* N-PDU 0, no extension header (why is this here?) */
576 ;
577
578 const char *gtp_pong_to_ggsn =
579 "32"
580 "02" /* type 02: Echo response */
581 "0006" /* length of 6 after header TEI */
582 "00000000" /* header TEI == 0 in Echo */
583 "cdef" /* same sequence nr */
584 "0000"
585 "0e23" /* Recovery with restart counter */
586 ;
587
588 struct osmo_sockaddr orig_ggsn_addr;
589 OSMO_ASSERT(osmo_sockaddr_init_udp(&orig_ggsn_addr,
590 "192.168.24.32", 321) == 0);
591 struct osmo_fd *sgsn_ofd = NULL;
592 send = gtphub_from_ggsns_handle_buf(hub, GTPH_PLANE_CTRL, &orig_ggsn_addr,
593 buf, msg(gtp_ping_from_ggsn), now,
594 &reply_buf, &sgsn_ofd, &to_addr);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200595 OSMO_ASSERT(send > 0);
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100596 OSMO_ASSERT(same_addr(&to_addr, &orig_ggsn_addr));
597 OSMO_ASSERT(reply_is(gtp_pong_to_ggsn));
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200598
599 struct gtphub_peer_port *ggsn_port =
600 gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL],
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100601 &sgsn_sender);
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100602 /* We don't record Echo peers. */
603 OSMO_ASSERT(!ggsn_port);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200604
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100605 now += EXPIRE_ALL;
606 gtphub_gc(hub, now);
607}
608
609
610#define MSG_PDP_CTX_REQ(len, seq, restart, imsi, tei_u, tei_c, apn, gsn_c, gsn_u) \
611 "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \
612 "10" /* type 16: Create PDP Context Request */ \
613 len /* msg length = 8 + len (2 octets) */ \
614 "00000000" /* No TEI yet */ \
615 seq /* Sequence nr (2 octets) */ \
616 "00" /* N-PDU 0 */ \
617 "00" /* No extensions */ \
618 /* IEs */ \
619 "0e" restart /* 14: Recovery = 96 (restart counter: 1 octet) */ \
620 "02" /* 2 = IMSI */ \
621 imsi /* (8 octets) */ \
622 "0f01" /* 15: Selection mode = MS provided APN, subscription not verified*/ \
623 "10" /* 16: TEI Data I */ \
624 tei_u /* (4 octets) */ \
625 "11" /* 17: TEI Control Plane */ \
626 tei_c /* (4 octets) */ \
627 "1400" /* 20: NSAPI = 0*/ \
628 "1a" /* 26: Charging Characteristics */ \
629 "0800" \
630 "80" /* 128: End User Address */ \
631 "0002" /* length = 2: empty PDP Address */ \
632 "f121" /* spare 0xf0, PDP organization 1, PDP type number 0x21 = 33 */ \
633 "83" /* 131: Access Point Name */ \
634 apn /* (2 octets length, N octets encoded APN-NI) */ \
635 "84" /* 132: Protocol Configuration Options */ \
636 "0015" /* length = 21 */ \
637 "80c0231101010011036d69670868656d6d656c6967" \
638 "85" /* 133: GSN Address */ \
639 gsn_c /* (2 octets length, N octets addr) */ \
640 "85" /* 133: GSN Address (second entry) */ \
641 gsn_u /* (2 octets length, N octets addr) */ \
642 "86" /* 134: MS International PSTN/ISDN Number (MSISDN) */ \
643 "0007" /* length */ \
644 "916407123254f6" /* 1946702123456(f) */ \
645 "87" /* 135: Quality of Service (QoS) Profile */ \
646 "0004" /* length */ \
647 "00" /* priority */ \
648 "0b921f" /* QoS profile data */
649
650#define MSG_PDP_CTX_RSP(len, tei_h, seq, restart, tei_u, tei_c, gsn_c, gsn_u) \
651 "32" \
652 "11" /* Create PDP Context Response */ \
653 len /* msg length = 8 + len (2 octets) */ \
654 tei_h /* destination TEI (sent in req above) */ \
655 seq /* mapped seq */ \
656 "00" "00" \
657 /* IEs */ \
658 "01" /* 1: Cause */ \
659 "80" /* value = 0b10000000 = response, no rejection. */ \
660 "08" /* 8: Reordering Required */ \
661 "00" /* not required. */ \
662 "0e" restart /* 14: Recovery = 1 */ \
663 "10" /* 16: TEI Data I */ \
664 tei_u \
665 "11" /* 17: TEI Control */ \
666 tei_c \
667 "7f" /* 127: Charging ID */ \
668 "00000001" \
669 "80" /* 128: End User Address */ \
670 "0006" /* length = 6 */ \
671 "f121" /* spare 0xf0, PDP organization 1, PDP type number 0x21 = 33 */ \
672 "7f000002" \
673 "84" /* 132: Protocol Configuration Options */ \
674 "0014" /* len = 20 */ \
675 "8080211002000010810608080808830600000000" \
676 "85" /* 133: GSN Address (Ctrl) */ \
677 gsn_c \
678 "85" /* 133: GSN Address (User) */ \
679 gsn_u \
680 "87" /* 135: Quality of Service (QoS) Profile */ \
681 "0004" /* length */ \
682 "00" /* priority */ \
683 "0b921f" /* QoS profile data */
684
685#define msg_from_sgsn_c(A,B,C,D) msg_from_sgsn(GTPH_PLANE_CTRL, A,B,C,D)
686#define msg_from_sgsn_u(A,B,C,D) msg_from_sgsn(GTPH_PLANE_USER, A,B,C,D)
687static int msg_from_sgsn(int plane_idx,
688 struct osmo_sockaddr *_sgsn_sender,
689 struct osmo_sockaddr *ggsn_receiver,
690 const char *hex_from_sgsn,
691 const char *hex_to_ggsn)
692{
693 struct osmo_fd *ggsn_ofd = NULL;
694 struct osmo_sockaddr ggsn_addr;
695 int send;
696 send = gtphub_from_sgsns_handle_buf(hub, plane_idx, _sgsn_sender, buf,
697 msg(hex_from_sgsn), now,
698 &reply_buf, &ggsn_ofd, &ggsn_addr);
699 LVL2_ASSERT(send > 0);
700 LVL2_ASSERT(same_addr(&ggsn_addr, ggsn_receiver));
701 LVL2_ASSERT(reply_is(hex_to_ggsn));
702 return 1;
703}
704
705#define msg_from_ggsn_c(A,B,C,D) msg_from_ggsn(GTPH_PLANE_CTRL, A,B,C,D)
706#define msg_from_ggsn_u(A,B,C,D) msg_from_ggsn(GTPH_PLANE_USER, A,B,C,D)
707static int msg_from_ggsn(int plane_idx,
708 struct osmo_sockaddr *ggsn_sender,
709 struct osmo_sockaddr *sgsn_receiver,
710 const char *msg_from_ggsn,
711 const char *msg_to_sgsn)
712{
713 struct osmo_fd *sgsn_ofd;
714 struct osmo_sockaddr sgsn_addr;
715 int send;
716 send = gtphub_from_ggsns_handle_buf(hub, plane_idx, ggsn_sender, buf,
717 msg(msg_from_ggsn), now,
718 &reply_buf, &sgsn_ofd, &sgsn_addr);
719 LVL2_ASSERT(send > 0);
720 LVL2_ASSERT(same_addr(&sgsn_addr, sgsn_receiver));
721 LVL2_ASSERT(reply_is(msg_to_sgsn));
722 return 1;
723}
724
725static int create_pdp_ctx()
726{
727 const char *gtp_req_from_sgsn = MSG_PDP_CTX_REQ("0068",
728 "abcd",
729 "60",
730 "42000121436587f9",
731 "00000123",
732 "00000321",
733 "0009""08696e7465726e6574", /* "(8)internet" */
734 "0004""c0a82a17", /* same as default sgsn_sender */
735 "0004""c0a82a17"
736 );
737 const char *gtp_req_to_ggsn = MSG_PDP_CTX_REQ("0068",
738 "6d31", /* mapped seq ("abcd") */
739 "60",
740 "42000121436587f9",
741 "00000001", /* mapped TEI Data I ("123") */
742 "00000001", /* mapped TEI Control ("321") */
743 "0009""08696e7465726e6574",
744 "0004""7f000201", /* replaced with gtphub's address ggsn ctrl */
745 "0004""7f000202" /* replaced with gtphub's address ggsn user */
746 );
747
748 LVL2_ASSERT(msg_from_sgsn_c(&sgsn_sender,
749 &resolved_ggsn_addr,
750 gtp_req_from_sgsn,
751 gtp_req_to_ggsn));
752 LVL2_ASSERT(was_resolved_for("240010123456789", "internet"));
753
754 const char *gtp_resp_from_ggsn = MSG_PDP_CTX_RSP("004e",
755 "00000001", /* destination TEI (sent in req above) */
756 "6d31", /* mapped seq */
757 "01", /* restart */
758 "00000567", /* TEI U */
759 "00000765", /* TEI C */
760 "0004""c0a82b22", /* GSN addresses */
761 "0004""c0a82b22" /* (== resolved_ggsn_addr) */
762 );
763 const char *gtp_resp_to_sgsn = MSG_PDP_CTX_RSP("004e",
764 "00000321", /* unmapped TEI ("001") */
765 "abcd", /* unmapped seq ("6d31") */
766 "01",
767 "00000002", /* mapped TEI from GGSN ("567") */
768 "00000002", /* mapped TEI from GGSN ("765") */
769 "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */
770 "0004""7f000102" /* gtphub's address towards SGSNs (User) */
771 );
772 LVL2_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr,
773 &sgsn_sender,
774 gtp_resp_from_ggsn,
775 gtp_resp_to_sgsn));
776
777 return 1;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200778}
779
780static void test_create_pdp_ctx(void)
781{
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100782 OSMO_ASSERT(setup_test_hub());
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200783
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100784 OSMO_ASSERT(create_pdp_ctx());
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200785
786 struct gtphub_peer_port *ggsn_port =
787 gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL],
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100788 &resolved_ggsn_addr);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200789 OSMO_ASSERT(ggsn_port);
790 struct gtphub_peer *ggsn = ggsn_port->peer_addr->peer;
791 /* now == 345; now + 30 == 375.
792 * seq mapping from above:
793 * 0xabcd == 43981 (sent in the packet)
794 * 0x6d31 == 27953 (harcoded seq mapping start val) */
795 OSMO_ASSERT(nr_map_is(&ggsn->seq_map, "(43981->27953@375), "));
796
797 /* now == 345; now + (6 * 60 * 60) == 21600 + 345 == 21945.
798 * 0x00000321 == 801 (TEI from SGSN Ctrl)
799 * 0x00000123 == 291 (TEI from SGSN User)
800 * 0x00000765 == 1893 (TEI from GGSN Ctrl)
801 * 0x00000567 == 1383 (TEI from GGSN User)
802 * Mapped TEIs should be 1 and 2. */
803 OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_CTRL], "(801->1@21945), (1893->2@21945), "));
804 OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_USER], "(291->1@21945), (1383->2@21945), "));
805
806 gtphub_gc(hub, now + EXPIRE_ALL);
807}
808
809static struct log_info_cat gtphub_categories[] = {
810 [DGTPHUB] = {
811 .name = "DGTPHUB",
812 .description = "GTP Hub",
813 .color = "\033[1;33m",
814 .enabled = 1, .loglevel = LOGL_NOTICE,
815 },
816};
817
818static struct log_info info = {
819 .cat = gtphub_categories,
820 .num_cat = ARRAY_SIZE(gtphub_categories),
821};
822
823int main(int argc, char **argv)
824{
825 osmo_init_logging(&info);
826 osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
827
828 test_nr_map_basic();
829 test_expiry();
830 test_echo();
831 test_create_pdp_ctx();
832 printf("Done\n");
833
834 talloc_report_full(osmo_gtphub_ctx, stderr);
835 OSMO_ASSERT(talloc_total_blocks(osmo_gtphub_ctx) == 1);
836 return 0;
837}
838