blob: 66d48314511a658446a6fc8638d6ed906456eb7e [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include "test.h"
21#include "server.h"
22
23enum
24{
25 NO = 0,
26 YES = 1,
27 SRV = 3,
28};
29
30#define NODELAY 0xFFFFFFFF
31#define SRV_DOMAIN "pjsip.lab.domain"
32#define MAX_THREADS 16
33
34#define THIS_FILE "ice_test.c"
35#define INDENT " "
36
37/* Client flags */
38enum
39{
40 WRONG_TURN = 1,
41 DEL_ON_ERR = 2,
42};
43
44
45/* Test results */
46struct test_result
47{
48 pj_status_t init_status; /* init successful? */
49 pj_status_t nego_status; /* negotiation successful? */
50 unsigned rx_cnt[4]; /* Number of data received */
51};
52
53/* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
54/* Test session configuration */
55struct test_cfg
56{
57 pj_ice_sess_role role; /* Role. */
58 unsigned comp_cnt; /* Component count */
59 unsigned enable_host; /* Enable host candidates */
60 unsigned enable_stun; /* Enable srflx candidates */
61 unsigned enable_turn; /* Enable turn candidates */
62 unsigned client_flag; /* Client flags */
63
64 unsigned answer_delay; /* Delay before sending SDP */
65 unsigned send_delay; /* unused */
66 unsigned destroy_delay; /* unused */
67
68 struct test_result expected;/* Expected result */
69
70 pj_bool_t nom_regular; /* Use regular nomination? */
71};
72
73/* ICE endpoint state */
74struct ice_ept
75{
76 struct test_cfg cfg; /* Configuratino. */
77 pj_ice_strans *ice; /* ICE stream transport */
78 struct test_result result;/* Test result. */
79
80 pj_str_t ufrag; /* username fragment. */
81 pj_str_t pass; /* password */
82};
83
84/* Session param */
85struct sess_param
86{
87 unsigned worker_cnt;
88 unsigned worker_timeout;
89 pj_bool_t worker_quit;
90
91 pj_bool_t destroy_after_create;
92 pj_bool_t destroy_after_one_done;
93};
94
95/* The test session */
96struct test_sess
97{
98 pj_pool_t *pool;
99 pj_stun_config *stun_cfg;
100 pj_dns_resolver *resolver;
101
102 struct sess_param *param;
103
104 test_server *server;
105
106 pj_thread_t *worker_threads[MAX_THREADS];
107
108 unsigned server_flag;
109 struct ice_ept caller;
110 struct ice_ept callee;
111};
112
113
114static void ice_on_rx_data(pj_ice_strans *ice_st,
115 unsigned comp_id,
116 void *pkt, pj_size_t size,
117 const pj_sockaddr_t *src_addr,
118 unsigned src_addr_len);
119static void ice_on_ice_complete(pj_ice_strans *ice_st,
120 pj_ice_strans_op op,
121 pj_status_t status);
122static void destroy_sess(struct test_sess *sess, unsigned wait_msec);
123
124/* Create ICE stream transport */
125static int create_ice_strans(struct test_sess *test_sess,
126 struct ice_ept *ept,
127 pj_ice_strans **p_ice)
128{
129 pj_ice_strans *ice;
130 pj_ice_strans_cb ice_cb;
131 pj_ice_strans_cfg ice_cfg;
132 pj_sockaddr hostip;
133 char serverip[PJ_INET6_ADDRSTRLEN];
134 pj_status_t status;
135
136 status = pj_gethostip(pj_AF_INET(), &hostip);
137 if (status != PJ_SUCCESS)
138 return -1030;
139
140 pj_sockaddr_print(&hostip, serverip, sizeof(serverip), 0);
141
142 /* Init callback structure */
143 pj_bzero(&ice_cb, sizeof(ice_cb));
144 ice_cb.on_rx_data = &ice_on_rx_data;
145 ice_cb.on_ice_complete = &ice_on_ice_complete;
146
147 /* Init ICE stream transport configuration structure */
148 pj_ice_strans_cfg_default(&ice_cfg);
149 pj_memcpy(&ice_cfg.stun_cfg, test_sess->stun_cfg, sizeof(pj_stun_config));
150 if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV)
151 ice_cfg.resolver = test_sess->resolver;
152
153 if (ept->cfg.enable_stun & YES) {
154 if ((ept->cfg.enable_stun & SRV) == SRV) {
155 ice_cfg.stun.server = pj_str(SRV_DOMAIN);
156 } else {
157 ice_cfg.stun.server = pj_str(serverip);
158 }
159 ice_cfg.stun.port = STUN_SERVER_PORT;
160 }
161
162 if (ept->cfg.enable_host == 0) {
163 ice_cfg.stun.max_host_cands = 0;
164 } else {
165 //ice_cfg.stun.no_host_cands = PJ_FALSE;
166 ice_cfg.stun.loop_addr = PJ_TRUE;
167 }
168
169
170 if (ept->cfg.enable_turn & YES) {
171 if ((ept->cfg.enable_turn & SRV) == SRV) {
172 ice_cfg.turn.server = pj_str(SRV_DOMAIN);
173 } else {
174 ice_cfg.turn.server = pj_str(serverip);
175 }
176 ice_cfg.turn.port = TURN_SERVER_PORT;
177 ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
178 ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
179 ice_cfg.turn.auth_cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
180 if (ept->cfg.client_flag & WRONG_TURN)
181 ice_cfg.turn.auth_cred.data.static_cred.username = pj_str("xxx");
182 else
183 ice_cfg.turn.auth_cred.data.static_cred.username = pj_str(TURN_USERNAME);
184 ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
185 ice_cfg.turn.auth_cred.data.static_cred.data = pj_str(TURN_PASSWD);
186 }
187
188 /* Create ICE stream transport */
189 status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt,
190 (void*)ept, &ice_cb,
191 &ice);
192 if (status != PJ_SUCCESS) {
193 app_perror(INDENT "err: pj_ice_strans_create()", status);
194 return status;
195 }
196
197 pj_create_unique_string(test_sess->pool, &ept->ufrag);
198 pj_create_unique_string(test_sess->pool, &ept->pass);
199
200 /* Looks alright */
201 *p_ice = ice;
202 return PJ_SUCCESS;
203}
204
205/* Create test session */
206static int create_sess(pj_stun_config *stun_cfg,
207 unsigned server_flag,
208 struct test_cfg *caller_cfg,
209 struct test_cfg *callee_cfg,
210 struct sess_param *test_param,
211 struct test_sess **p_sess)
212{
213 pj_pool_t *pool;
214 struct test_sess *sess;
215 pj_str_t ns_ip;
216 pj_uint16_t ns_port;
217 unsigned flags;
218 pj_status_t status;
219
220 /* Create session structure */
221 pool = pj_pool_create(mem, "testsess", 512, 512, NULL);
222 sess = PJ_POOL_ZALLOC_T(pool, struct test_sess);
223 sess->pool = pool;
224 sess->stun_cfg = stun_cfg;
225 sess->param = test_param;
226
227 pj_memcpy(&sess->caller.cfg, caller_cfg, sizeof(*caller_cfg));
228 sess->caller.result.init_status = sess->caller.result.nego_status = PJ_EPENDING;
229
230 pj_memcpy(&sess->callee.cfg, callee_cfg, sizeof(*callee_cfg));
231 sess->callee.result.init_status = sess->callee.result.nego_status = PJ_EPENDING;
232
233 /* Create server */
234 flags = server_flag;
235 status = create_test_server(stun_cfg, flags, SRV_DOMAIN, &sess->server);
236 if (status != PJ_SUCCESS) {
237 app_perror(INDENT "error: create_test_server()", status);
238 destroy_sess(sess, 500);
239 return -10;
240 }
241 sess->server->turn_respond_allocate =
242 sess->server->turn_respond_refresh = PJ_TRUE;
243
244 /* Create resolver */
245 status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap,
246 stun_cfg->ioqueue, &sess->resolver);
247 if (status != PJ_SUCCESS) {
248 app_perror(INDENT "error: pj_dns_resolver_create()", status);
249 destroy_sess(sess, 500);
250 return -20;
251 }
252
253 ns_ip = pj_str("127.0.0.1");
254 ns_port = (pj_uint16_t)DNS_SERVER_PORT;
255 status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port);
256 if (status != PJ_SUCCESS) {
257 app_perror( INDENT "error: pj_dns_resolver_set_ns()", status);
258 destroy_sess(sess, 500);
259 return -21;
260 }
261
262 /* Create caller ICE stream transport */
263 status = create_ice_strans(sess, &sess->caller, &sess->caller.ice);
264 if (status != PJ_SUCCESS) {
265 destroy_sess(sess, 500);
266 return -30;
267 }
268
269 /* Create callee ICE stream transport */
270 status = create_ice_strans(sess, &sess->callee, &sess->callee.ice);
271 if (status != PJ_SUCCESS) {
272 destroy_sess(sess, 500);
273 return -40;
274 }
275
276 *p_sess = sess;
277 return 0;
278}
279
280/* Destroy test session */
281static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
282{
283 unsigned i;
284
285 if (sess->caller.ice) {
286 pj_ice_strans_destroy(sess->caller.ice);
287 sess->caller.ice = NULL;
288 }
289
290 if (sess->callee.ice) {
291 pj_ice_strans_destroy(sess->callee.ice);
292 sess->callee.ice = NULL;
293 }
294
295 sess->param->worker_quit = PJ_TRUE;
296 for (i=0; i<sess->param->worker_cnt; ++i) {
297 if (sess->worker_threads[i])
298 pj_thread_join(sess->worker_threads[i]);
299 }
300
301 poll_events(sess->stun_cfg, wait_msec, PJ_FALSE);
302
303 if (sess->resolver) {
304 pj_dns_resolver_destroy(sess->resolver, PJ_FALSE);
305 sess->resolver = NULL;
306 }
307
308 if (sess->server) {
309 destroy_test_server(sess->server);
310 sess->server = NULL;
311 }
312
313 if (sess->pool) {
314 pj_pool_t *pool = sess->pool;
315 sess->pool = NULL;
316 pj_pool_release(pool);
317 }
318}
319
320static void ice_on_rx_data(pj_ice_strans *ice_st,
321 unsigned comp_id,
322 void *pkt, pj_size_t size,
323 const pj_sockaddr_t *src_addr,
324 unsigned src_addr_len)
325{
326 struct ice_ept *ept;
327
328 PJ_UNUSED_ARG(pkt);
329 PJ_UNUSED_ARG(size);
330 PJ_UNUSED_ARG(src_addr);
331 PJ_UNUSED_ARG(src_addr_len);
332
333 ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
334 ept->result.rx_cnt[comp_id]++;
335}
336
337
338static void ice_on_ice_complete(pj_ice_strans *ice_st,
339 pj_ice_strans_op op,
340 pj_status_t status)
341{
342 struct ice_ept *ept;
343
344 ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
345 switch (op) {
346 case PJ_ICE_STRANS_OP_INIT:
347 ept->result.init_status = status;
348 if (status != PJ_SUCCESS && (ept->cfg.client_flag & DEL_ON_ERR)) {
349 pj_ice_strans_destroy(ice_st);
350 ept->ice = NULL;
351 }
352 break;
353 case PJ_ICE_STRANS_OP_NEGOTIATION:
354 ept->result.nego_status = status;
355 break;
356 case PJ_ICE_STRANS_OP_KEEP_ALIVE:
357 /* keep alive failed? */
358 break;
359 default:
360 pj_assert(!"Unknown op");
361 }
362}
363
364
365/* Start ICE negotiation on the endpoint, based on parameter from
366 * the other endpoint.
367 */
368static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote)
369{
370 pj_ice_sess_cand rcand[32];
371 unsigned i, rcand_cnt = 0;
372 pj_status_t status;
373
374 /* Enum remote candidates */
375 for (i=0; i<remote->cfg.comp_cnt; ++i) {
376 unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt;
377 status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt);
378 if (status != PJ_SUCCESS) {
379 app_perror(INDENT "err: pj_ice_strans_enum_cands()", status);
380 return status;
381 }
382 rcand_cnt += cnt;
383 }
384
385 status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass,
386 rcand_cnt, rcand);
387 if (status != PJ_SUCCESS) {
388 app_perror(INDENT "err: pj_ice_strans_start_ice()", status);
389 return status;
390 }
391
392 return PJ_SUCCESS;
393}
394
395
396/* Check that the pair in both agents are matched */
397static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
398 int start_err)
399{
400 unsigned i, min_cnt, max_cnt;
401
402 if (ept1->cfg.comp_cnt < ept2->cfg.comp_cnt) {
403 min_cnt = ept1->cfg.comp_cnt;
404 max_cnt = ept2->cfg.comp_cnt;
405 } else {
406 min_cnt = ept2->cfg.comp_cnt;
407 max_cnt = ept1->cfg.comp_cnt;
408 }
409
410 /* Must have valid pair for common components */
411 for (i=0; i<min_cnt; ++i) {
412 const pj_ice_sess_check *c1;
413 const pj_ice_sess_check *c2;
414
415 c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1);
416 if (c1 == NULL) {
417 PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice1 "
418 "component %d", i+1));
419 return start_err - 2;
420 }
421
422 c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1);
423 if (c2 == NULL) {
424 PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice2 "
425 "component %d", i+1));
426 return start_err - 4;
427 }
428
429 if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) {
430 PJ_LOG(3,(THIS_FILE, INDENT "err: candidate pair does not match "
431 "for component %d", i+1));
432 return start_err - 6;
433 }
434 }
435
436 /* Extra components must not have valid pair */
437 for (; i<max_cnt; ++i) {
438 if (ept1->cfg.comp_cnt>i &&
439 pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL)
440 {
441 PJ_LOG(3,(THIS_FILE, INDENT "err: ice1 shouldn't have valid pair "
442 "for component %d", i+1));
443 return start_err - 8;
444 }
445 if (ept2->cfg.comp_cnt>i &&
446 pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL)
447 {
448 PJ_LOG(3,(THIS_FILE, INDENT "err: ice2 shouldn't have valid pair "
449 "for component %d", i+1));
450 return start_err - 9;
451 }
452 }
453
454 return 0;
455}
456
457
458#define WAIT_UNTIL(timeout,expr, RC) { \
459 pj_time_val t0, t; \
460 pj_gettimeofday(&t0); \
461 RC = -1; \
462 for (;;) { \
463 poll_events(stun_cfg, 10, PJ_FALSE); \
464 pj_gettimeofday(&t); \
465 if (expr) { \
466 rc = PJ_SUCCESS; \
467 break; \
468 } \
469 PJ_TIME_VAL_SUB(t, t0); \
470 if ((unsigned)PJ_TIME_VAL_MSEC(t) >= (timeout)) \
471 break; \
472 } \
473 }
474
475int worker_thread_proc(void *data)
476{
477 pj_status_t rc;
478 struct test_sess *sess = (struct test_sess *) data;
479 pj_stun_config *stun_cfg = sess->stun_cfg;
480
481 /* Wait until negotiation is complete on both endpoints */
482#define ALL_DONE (sess->param->worker_quit || \
483 (sess->caller.result.nego_status!=PJ_EPENDING && \
484 sess->callee.result.nego_status!=PJ_EPENDING))
485 WAIT_UNTIL(sess->param->worker_timeout, ALL_DONE, rc);
486
487 return 0;
488}
489
490static int perform_test2(const char *title,
491 pj_stun_config *stun_cfg,
492 unsigned server_flag,
493 struct test_cfg *caller_cfg,
494 struct test_cfg *callee_cfg,
495 struct sess_param *test_param)
496{
497 pjlib_state pjlib_state;
498 struct test_sess *sess;
499 unsigned i;
500 int rc;
501
502 PJ_LOG(3,(THIS_FILE, INDENT "%s", title));
503
504 capture_pjlib_state(stun_cfg, &pjlib_state);
505
506 rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, test_param, &sess);
507 if (rc != 0)
508 return rc;
509
510#define ALL_READY (sess->caller.result.init_status!=PJ_EPENDING && \
511 sess->callee.result.init_status!=PJ_EPENDING)
512
513 /* Wait until both ICE transports are initialized */
514 WAIT_UNTIL(30000, ALL_READY, rc);
515
516 if (!ALL_READY) {
517 PJ_LOG(3,(THIS_FILE, INDENT "err: init timed-out"));
518 destroy_sess(sess, 500);
519 return -100;
520 }
521
522 if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) {
523 app_perror(INDENT "err: caller init", sess->caller.result.init_status);
524 destroy_sess(sess, 500);
525 return -102;
526 }
527 if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) {
528 app_perror(INDENT "err: callee init", sess->callee.result.init_status);
529 destroy_sess(sess, 500);
530 return -104;
531 }
532
533 /* Failure condition */
534 if (sess->caller.result.init_status != PJ_SUCCESS ||
535 sess->callee.result.init_status != PJ_SUCCESS)
536 {
537 rc = 0;
538 goto on_return;
539 }
540 /* Init ICE on caller */
541 rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role,
542 &sess->caller.ufrag, &sess->caller.pass);
543 if (rc != PJ_SUCCESS) {
544 app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc);
545 destroy_sess(sess, 500);
546 return -100;
547 }
548
549 /* Init ICE on callee */
550 rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role,
551 &sess->callee.ufrag, &sess->callee.pass);
552 if (rc != PJ_SUCCESS) {
553 app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc);
554 destroy_sess(sess, 500);
555 return -110;
556 }
557 /* Start ICE on callee */
558 rc = start_ice(&sess->callee, &sess->caller);
559 if (rc != PJ_SUCCESS) {
560 destroy_sess(sess, 500);
561 return -120;
562 }
563 /* Wait for callee's answer_delay */
564 poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE);
565 /* Start ICE on caller */
566 rc = start_ice(&sess->caller, &sess->callee);
567 if (rc != PJ_SUCCESS) {
568 destroy_sess(sess, 500);
569 return -130;
570 }
571
572 for (i=0; i<sess->param->worker_cnt; ++i) {
573 pj_status_t status;
574
575 status = pj_thread_create(sess->pool, "worker_thread",
576 worker_thread_proc, sess, 0, 0,
577 &sess->worker_threads[i]);
578 if (status != PJ_SUCCESS) {
579 PJ_LOG(3,(THIS_FILE, INDENT "err: create thread"));
580 destroy_sess(sess, 500);
581 return -135;
582 }
583 }
584
585 if (sess->param->destroy_after_create)
586 goto on_destroy;
587
588 if (sess->param->destroy_after_one_done) {
589 while (sess->caller.result.init_status==PJ_EPENDING &&
590 sess->callee.result.init_status==PJ_EPENDING)
591 {
592 if (sess->param->worker_cnt)
593 pj_thread_sleep(0);
594 else
595 poll_events(stun_cfg, 0, PJ_FALSE);
596 }
597 goto on_destroy;
598 }
599
600 WAIT_UNTIL(30000, ALL_DONE, rc);
601 if (!ALL_DONE) {
602 PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out"));
603 destroy_sess(sess, 500);
604 return -140;
605 }
606
607 if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) {
608 app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status);
609 destroy_sess(sess, 500);
610 return -150;
611 }
612
613 if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) {
614 app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status);
615 destroy_sess(sess, 500);
616 return -160;
617 }
618
619 /* Verify that both agents have agreed on the same pair */
620 rc = check_pair(&sess->caller, &sess->callee, -170);
621 if (rc != 0) {
622 destroy_sess(sess, 500);
623 return rc;
624 }
625 rc = check_pair(&sess->callee, &sess->caller, -180);
626 if (rc != 0) {
627 destroy_sess(sess, 500);
628 return rc;
629 }
630
631 /* Looks like everything is okay */
632on_destroy:
633
634 /* Destroy ICE stream transports first to let it de-allocate
635 * TURN relay (otherwise there'll be timer/memory leak, unless
636 * we wait for long time in the last poll_events() below).
637 */
638 if (sess->caller.ice) {
639 pj_ice_strans_destroy(sess->caller.ice);
640 sess->caller.ice = NULL;
641 }
642
643 if (sess->callee.ice) {
644 pj_ice_strans_destroy(sess->callee.ice);
645 sess->callee.ice = NULL;
646 }
647
648on_return:
649 /* Wait.. */
650 poll_events(stun_cfg, 200, PJ_FALSE);
651
652 /* Now destroy everything */
653 destroy_sess(sess, 500);
654
655 /* Flush events */
656 poll_events(stun_cfg, 100, PJ_FALSE);
657
658 rc = check_pjlib_state(stun_cfg, &pjlib_state);
659 if (rc != 0) {
660 return rc;
661 }
662
663 return rc;
664}
665
666static int perform_test(const char *title,
667 pj_stun_config *stun_cfg,
668 unsigned server_flag,
669 struct test_cfg *caller_cfg,
670 struct test_cfg *callee_cfg)
671{
672 struct sess_param test_param;
673
674 pj_bzero(&test_param, sizeof(test_param));
675 return perform_test2(title, stun_cfg, server_flag, caller_cfg,
676 callee_cfg, &test_param);
677}
678
679#define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED
680#define ROLE2 PJ_ICE_SESS_ROLE_CONTROLLING
681
682int ice_test(void)
683{
684 pj_pool_t *pool;
685 pj_stun_config stun_cfg;
686 unsigned i;
687 int rc;
688 struct sess_cfg_t {
689 const char *title;
690 unsigned server_flag;
691 struct test_cfg ua1;
692 struct test_cfg ua2;
693 } sess_cfg[] =
694 {
695 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
696 {
697 "hosts candidates only",
698 0xFFFF,
699 {ROLE1, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
700 {ROLE2, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
701 },
702 {
703 "host and srflxes",
704 0xFFFF,
705 {ROLE1, 1, YES, YES, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
706 {ROLE2, 1, YES, YES, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
707 },
708 {
709 "host vs relay",
710 0xFFFF,
711 {ROLE1, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
712 {ROLE2, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
713 },
714 {
715 "relay vs host",
716 0xFFFF,
717 {ROLE1, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
718 {ROLE2, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
719 },
720 {
721 "relay vs relay",
722 0xFFFF,
723 {ROLE1, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
724 {ROLE2, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
725 },
726 {
727 "all candidates",
728 0xFFFF,
729 {ROLE1, 1, YES, YES, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
730 {ROLE2, 1, YES, YES, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
731 },
732 };
733
734 pool = pj_pool_create(mem, NULL, 512, 512, NULL);
735 rc = create_stun_config(pool, &stun_cfg);
736 if (rc != PJ_SUCCESS) {
737 pj_pool_release(pool);
738 return -7;
739 }
740
741 /* Simple test first with host candidate */
742 if (1) {
743 struct sess_cfg_t cfg =
744 {
745 "Basic with host candidates",
746 0x0,
747 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
748 {ROLE1, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
749 {ROLE2, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
750 };
751
752 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
753 &cfg.ua1, &cfg.ua2);
754 if (rc != 0)
755 goto on_return;
756
757 cfg.ua1.comp_cnt = 2;
758 cfg.ua2.comp_cnt = 2;
759 rc = perform_test("Basic with host candidates, 2 components",
760 &stun_cfg, cfg.server_flag,
761 &cfg.ua1, &cfg.ua2);
762 if (rc != 0)
763 goto on_return;
764 }
765
766 /* Simple test first with srflx candidate */
767 if (1) {
768 struct sess_cfg_t cfg =
769 {
770 "Basic with srflx candidates",
771 0xFFFF,
772 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
773 {ROLE1, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
774 {ROLE2, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
775 };
776
777 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
778 &cfg.ua1, &cfg.ua2);
779 if (rc != 0)
780 goto on_return;
781
782 cfg.ua1.comp_cnt = 2;
783 cfg.ua2.comp_cnt = 2;
784
785 rc = perform_test("Basic with srflx candidates, 2 components",
786 &stun_cfg, cfg.server_flag,
787 &cfg.ua1, &cfg.ua2);
788 if (rc != 0)
789 goto on_return;
790 }
791
792 /* Simple test with relay candidate */
793 if (1) {
794 struct sess_cfg_t cfg =
795 {
796 "Basic with relay candidates",
797 0xFFFF,
798 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
799 {ROLE1, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
800 {ROLE2, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
801 };
802
803 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
804 &cfg.ua1, &cfg.ua2);
805 if (rc != 0)
806 goto on_return;
807
808 cfg.ua1.comp_cnt = 2;
809 cfg.ua2.comp_cnt = 2;
810
811 rc = perform_test("Basic with relay candidates, 2 components",
812 &stun_cfg, cfg.server_flag,
813 &cfg.ua1, &cfg.ua2);
814 if (rc != 0)
815 goto on_return;
816 }
817
818 /* Failure test with STUN resolution */
819 if (1) {
820 struct sess_cfg_t cfg =
821 {
822 "STUN resolution failure",
823 0x0,
824 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
825 {ROLE1, 2, NO, YES, NO, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}},
826 {ROLE2, 2, NO, YES, NO, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}}
827 };
828
829 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
830 &cfg.ua1, &cfg.ua2);
831 if (rc != 0)
832 goto on_return;
833
834 cfg.ua1.client_flag |= DEL_ON_ERR;
835 cfg.ua2.client_flag |= DEL_ON_ERR;
836
837 rc = perform_test("STUN resolution failure with destroy on callback",
838 &stun_cfg, cfg.server_flag,
839 &cfg.ua1, &cfg.ua2);
840 if (rc != 0)
841 goto on_return;
842 }
843
844 /* Failure test with TURN resolution */
845 if (1) {
846 struct sess_cfg_t cfg =
847 {
848 "TURN allocation failure",
849 0xFFFF,
850 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
851 {ROLE1, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_STATUS_FROM_STUN_CODE(401), -1}},
852 {ROLE2, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_STATUS_FROM_STUN_CODE(401), -1}}
853 };
854
855 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
856 &cfg.ua1, &cfg.ua2);
857 if (rc != 0)
858 goto on_return;
859
860 cfg.ua1.client_flag |= DEL_ON_ERR;
861 cfg.ua2.client_flag |= DEL_ON_ERR;
862
863 rc = perform_test("TURN allocation failure with destroy on callback",
864 &stun_cfg, cfg.server_flag,
865 &cfg.ua1, &cfg.ua2);
866 if (rc != 0)
867 goto on_return;
868 }
869
870
871 /* STUN failure, testing TURN deallocation */
872 if (1) {
873 struct sess_cfg_t cfg =
874 {
875 "STUN failure, testing TURN deallocation",
876 0xFFFF & (~(CREATE_STUN_SERVER)),
877 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
878 {ROLE1, 1, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}},
879 {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}}
880 };
881
882 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
883 &cfg.ua1, &cfg.ua2);
884 if (rc != 0)
885 goto on_return;
886
887 cfg.ua1.client_flag |= DEL_ON_ERR;
888 cfg.ua2.client_flag |= DEL_ON_ERR;
889
890 rc = perform_test("STUN failure, testing TURN deallocation (cb)",
891 &stun_cfg, cfg.server_flag,
892 &cfg.ua1, &cfg.ua2);
893 if (rc != 0)
894 goto on_return;
895 }
896
897 rc = 0;
898 /* Iterate each test item */
899 for (i=0; i<PJ_ARRAY_SIZE(sess_cfg); ++i) {
900 struct sess_cfg_t *cfg = &sess_cfg[i];
901 unsigned delay[] = { 50, 2000 };
902 unsigned d;
903
904 PJ_LOG(3,(THIS_FILE, " %s", cfg->title));
905
906 /* For each test item, test with various answer delay */
907 for (d=0; d<PJ_ARRAY_SIZE(delay); ++d) {
908 struct role_t {
909 pj_ice_sess_role ua1;
910 pj_ice_sess_role ua2;
911 } role[] =
912 {
913 { ROLE1, ROLE2},
914 { ROLE2, ROLE1},
915 { ROLE1, ROLE1},
916 { ROLE2, ROLE2}
917 };
918 unsigned j;
919
920 cfg->ua1.answer_delay = delay[d];
921 cfg->ua2.answer_delay = delay[d];
922
923 /* For each test item, test with role conflict scenarios */
924 for (j=0; j<PJ_ARRAY_SIZE(role); ++j) {
925 unsigned k1;
926
927 cfg->ua1.role = role[j].ua1;
928 cfg->ua2.role = role[j].ua2;
929
930 /* For each test item, test with different number of components */
931 for (k1=1; k1<=2; ++k1) {
932 unsigned k2;
933
934 cfg->ua1.comp_cnt = k1;
935
936 for (k2=1; k2<=2; ++k2) {
937 char title[120];
938
939 sprintf(title,
940 "%s/%s, %dms answer delay, %d vs %d components",
941 pj_ice_sess_role_name(role[j].ua1),
942 pj_ice_sess_role_name(role[j].ua2),
943 delay[d], k1, k2);
944
945 cfg->ua2.comp_cnt = k2;
946 rc = perform_test(title, &stun_cfg, cfg->server_flag,
947 &cfg->ua1, &cfg->ua2);
948 if (rc != 0)
949 goto on_return;
950 }
951 }
952 }
953 }
954 }
955
956on_return:
957 destroy_stun_config(&stun_cfg);
958 pj_pool_release(pool);
959 return rc;
960}
961
962int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit)
963{
964 struct sess_cfg_t {
965 const char *title;
966 unsigned server_flag;
967 struct test_cfg ua1;
968 struct test_cfg ua2;
969 } cfg =
970 {
971 "Concurrency test",
972 0xFFFF,
973 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
974 {ROLE1, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
975 {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
976 };
977 struct sess_param test_param;
978 int rc;
979
980
981 /* test a: destroy as soon as nego starts */
982 cfg.title = " ice test a: immediate destroy";
983 pj_bzero(&test_param, sizeof(test_param));
984 test_param.worker_cnt = 4;
985 test_param.worker_timeout = 1000;
986 test_param.destroy_after_create = PJ_TRUE;
987
988 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
989 &cfg.ua1, &cfg.ua2, &test_param);
990 if (rc != 0 && err_quit)
991 return rc;
992
993 /* test b: destroy as soon as one is done */
994 cfg.title = " ice test b: destroy after 1 success";
995 test_param.destroy_after_create = PJ_FALSE;
996 test_param.destroy_after_one_done = PJ_TRUE;
997
998 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
999 &cfg.ua1, &cfg.ua2, &test_param);
1000 if (rc != 0 && err_quit)
1001 return rc;
1002
1003 /* test c: normal */
1004 cfg.title = " ice test c: normal flow";
1005 pj_bzero(&test_param, sizeof(test_param));
1006 test_param.worker_cnt = 4;
1007 test_param.worker_timeout = 1000;
1008
1009 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
1010 &cfg.ua1, &cfg.ua2, &test_param);
1011 if (rc != 0 && err_quit)
1012 return rc;
1013
1014 return 0;
1015}
1016
1017int ice_conc_test(void)
1018{
1019 const unsigned LOOP = 100;
1020 pj_pool_t *pool;
1021 pj_stun_config stun_cfg;
1022 unsigned i;
1023 int rc;
1024
1025 pool = pj_pool_create(mem, NULL, 512, 512, NULL);
1026 rc = create_stun_config(pool, &stun_cfg);
1027 if (rc != PJ_SUCCESS) {
1028 pj_pool_release(pool);
1029 return -7;
1030 }
1031
1032 for (i = 0; i < LOOP; i++) {
1033 PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP));
1034 rc = ice_one_conc_test(&stun_cfg, PJ_TRUE);
1035 if (rc)
1036 break;
1037 }
1038
1039 /* Avoid compiler warning */
1040 goto on_return;
1041
1042on_return:
1043 destroy_stun_config(&stun_cfg);
1044 pj_pool_release(pool);
1045
1046 return rc;
1047}