blob: efa2311a1648c55bd476935bd0cfc5303bb0f6a0 [file] [log] [blame]
Benny Prijonofb9d9872007-03-21 22:05:58 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20
21#define THIS_FILE "ice.c"
22
23
24struct ice_data
25{
Benny Prijono2532b9d2007-03-22 01:16:37 +000026 const char *obj_name;
Benny Prijonofb9d9872007-03-21 22:05:58 +000027 pj_bool_t complete;
28 pj_status_t err_code;
29 unsigned rx_rtp_cnt;
30 unsigned rx_rtcp_cnt;
Benny Prijono2532b9d2007-03-22 01:16:37 +000031
Benny Prijonofd7f1472007-03-22 11:59:03 +000032 unsigned rx_rtp_count;
33 char last_rx_rtp_data[32];
34 unsigned rx_rtcp_count;
35 char last_rx_rtcp_data[32];
Benny Prijonofb9d9872007-03-21 22:05:58 +000036};
37
38static pj_stun_config stun_cfg;
39
40static void on_ice_complete(pj_icemt *icemt,
41 pj_status_t status)
42{
43 struct ice_data *id = (struct ice_data*) icemt->user_data;
44 id->complete = PJ_TRUE;
45 id->err_code = status;
Benny Prijono2532b9d2007-03-22 01:16:37 +000046 PJ_LOG(3,(THIS_FILE, " ICE %s complete %s", id->obj_name,
47 (status==PJ_SUCCESS ? "successfully" : "with failure")));
Benny Prijonofb9d9872007-03-21 22:05:58 +000048}
49
50
51static void on_rx_rtp(pj_icemt *icemt,
52 void *pkt, pj_size_t size,
53 const pj_sockaddr_t *src_addr,
54 unsigned src_addr_len)
55{
56 struct ice_data *id = (struct ice_data*) icemt->user_data;
Benny Prijono2532b9d2007-03-22 01:16:37 +000057
Benny Prijonofb9d9872007-03-21 22:05:58 +000058 id->rx_rtp_cnt++;
Benny Prijonofd7f1472007-03-22 11:59:03 +000059 pj_memcpy(id->last_rx_rtp_data, pkt, size);
60 id->last_rx_rtp_data[size] = '\0';
Benny Prijono2532b9d2007-03-22 01:16:37 +000061
62 PJ_UNUSED_ARG(src_addr);
63 PJ_UNUSED_ARG(src_addr_len);
Benny Prijonofb9d9872007-03-21 22:05:58 +000064}
65
66
67static void on_rx_rtcp(pj_icemt *icemt,
68 void *pkt, pj_size_t size,
69 const pj_sockaddr_t *src_addr,
70 unsigned src_addr_len)
71{
72 struct ice_data *id = (struct ice_data*) icemt->user_data;
Benny Prijono2532b9d2007-03-22 01:16:37 +000073
Benny Prijonofb9d9872007-03-21 22:05:58 +000074 id->rx_rtcp_cnt++;
Benny Prijonofd7f1472007-03-22 11:59:03 +000075 pj_memcpy(id->last_rx_rtcp_data, pkt, size);
76 id->last_rx_rtcp_data[size] = '\0';
Benny Prijono2532b9d2007-03-22 01:16:37 +000077
78 PJ_UNUSED_ARG(src_addr);
79 PJ_UNUSED_ARG(src_addr_len);
Benny Prijonofb9d9872007-03-21 22:05:58 +000080}
81
82
83static void handle_events(unsigned msec_timeout)
84{
85 pj_time_val delay;
86
87 pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
88
89 delay.sec = 0;
90 delay.msec = msec_timeout;
91 pj_time_val_normalize(&delay);
92
93 pj_ioqueue_poll(stun_cfg.ioqueue, &delay);
94}
95
96
97/* Basic create and destroy test */
98static int ice_basic_create_destroy_test()
99{
100 pj_icemt *im;
101 pj_ice *ice;
102 pj_icemt_cb icemt_cb;
103 pj_status_t status;
104
105 PJ_LOG(3,(THIS_FILE, "...basic create/destroy"));
106
107 pj_bzero(&icemt_cb, sizeof(icemt_cb));
108 icemt_cb.on_ice_complete = &on_ice_complete;
Benny Prijonofd7f1472007-03-22 11:59:03 +0000109 icemt_cb.on_rx_rtp = &on_rx_rtp;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000110 icemt_cb.on_rx_rtcp = &on_rx_rtcp;
111
112 status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING,
113 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im);
114 if (status != PJ_SUCCESS)
115 return -10;
116
117 ice = im->ice;
118
119 pj_icemt_destroy(im);
120
121 return 0;
122}
123
124
125static pj_status_t set_remote_list(pj_icemt *src, pj_icemt *dst)
126{
127 unsigned i, count;
128 unsigned cand_id[PJ_ICE_MAX_CAND];
129 pj_ice_cand cand[PJ_ICE_MAX_CAND];
130 pj_status_t status;
131
132 count = PJ_ARRAY_SIZE(cand_id);
133 status = pj_ice_enum_cands(src->ice, &count, cand_id);
134 if (status != PJ_SUCCESS)
135 return status;
136
137 for (i=0; i<count; ++i) {
138 pj_ice_cand *p_cand;
139 status = pj_ice_get_cand(src->ice, cand_id[i], &p_cand);
140 if (status != PJ_SUCCESS)
141 return status;
142
143 pj_memcpy(&cand[i], p_cand, sizeof(pj_ice_cand));
144 }
145
146 status = pj_ice_create_check_list(dst->ice, count, cand);
147 return status;
148}
149
150
Benny Prijono2532b9d2007-03-22 01:16:37 +0000151/* Perform ICE test with the following parameters:
152 *
153 * - title: The title of the test
154 * - ocand_cnt,
155 * ocand Additional candidates to be added to offerer
156 * - acand_cnt,
157 * acand Additional candidates to be added to answerer
158 *
Benny Prijonofd7f1472007-03-22 11:59:03 +0000159 * The additional candidates are normally invalid candidates, meaning
160 * they won't be reachable by the agents. They are used to "confuse"
Benny Prijono2532b9d2007-03-22 01:16:37 +0000161 * ICE processing.
162 */
163static int perform_ice_test(const char *title,
Benny Prijonofd7f1472007-03-22 11:59:03 +0000164 unsigned wait_before_send,
165 unsigned max_total_time,
Benny Prijono2532b9d2007-03-22 01:16:37 +0000166 unsigned ocand_cnt,
167 const pj_ice_cand ocand[],
168 unsigned acand_cnt,
169 const pj_ice_cand acand[])
Benny Prijonofb9d9872007-03-21 22:05:58 +0000170{
171 pj_icemt *im1, *im2;
172 pj_icemt_cb icemt_cb;
173 struct ice_data *id1, *id2;
Benny Prijono2532b9d2007-03-22 01:16:37 +0000174 pj_timestamp t_start, t_end;
175 pj_ice_cand *rcand;
Benny Prijonofd7f1472007-03-22 11:59:03 +0000176 pj_str_t data_from_offerer, data_from_answerer;
Benny Prijono2532b9d2007-03-22 01:16:37 +0000177 unsigned i;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000178 pj_status_t status;
179
Benny Prijono2532b9d2007-03-22 01:16:37 +0000180 PJ_LOG(3,(THIS_FILE, "...%s", title));
Benny Prijonofb9d9872007-03-21 22:05:58 +0000181
182 pj_bzero(&icemt_cb, sizeof(icemt_cb));
183 icemt_cb.on_ice_complete = &on_ice_complete;
Benny Prijonofd7f1472007-03-22 11:59:03 +0000184 icemt_cb.on_rx_rtp = &on_rx_rtp;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000185 icemt_cb.on_rx_rtcp = &on_rx_rtcp;
186
187 /* Create first ICE */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000188 status = pj_icemt_create(&stun_cfg, "offerer", PJ_ICE_ROLE_CONTROLLING,
Benny Prijonofb9d9872007-03-21 22:05:58 +0000189 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im1);
190 if (status != PJ_SUCCESS)
191 return -20;
192
193 id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data);
Benny Prijono2532b9d2007-03-22 01:16:37 +0000194 id1->obj_name = "offerer";
Benny Prijonofb9d9872007-03-21 22:05:58 +0000195 im1->user_data = id1;
196
Benny Prijono2532b9d2007-03-22 01:16:37 +0000197 /* Add additional candidates */
198 for (i=0; i<ocand_cnt; ++i) {
199 status = pj_ice_add_cand(im1->ice, 1, ocand[i].type, 65535,
200 &ocand[i].foundation, &ocand[i].addr,
201 &ocand[i].base_addr, &ocand[i].srv_addr,
202 sizeof(pj_sockaddr_in), NULL);
203 if (status != PJ_SUCCESS)
204 return -22;
205 }
206
Benny Prijonofb9d9872007-03-21 22:05:58 +0000207 /* Create second ICE */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000208 status = pj_icemt_create(&stun_cfg, "answerer", PJ_ICE_ROLE_CONTROLLED,
Benny Prijonofb9d9872007-03-21 22:05:58 +0000209 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im2);
210 if (status != PJ_SUCCESS)
211 return -25;
212
213 id2 = PJ_POOL_ZALLOC_T(im2->pool, struct ice_data);
Benny Prijono2532b9d2007-03-22 01:16:37 +0000214 id2->obj_name = "answerer";
Benny Prijonofb9d9872007-03-21 22:05:58 +0000215 im2->user_data = id2;
216
Benny Prijono2532b9d2007-03-22 01:16:37 +0000217 /* Add additional candidates */
218 for (i=0; i<acand_cnt; ++i) {
219 status = pj_ice_add_cand(im1->ice, 1, acand[i].type, 65535,
220 &acand[i].foundation, &acand[i].addr,
221 &acand[i].base_addr, &acand[i].srv_addr,
222 sizeof(pj_sockaddr_in), NULL);
223 if (status != PJ_SUCCESS)
224 return -22;
225 }
226
227 /* Set credentials */
Benny Prijonofb9d9872007-03-21 22:05:58 +0000228 {
Benny Prijono2532b9d2007-03-22 01:16:37 +0000229 pj_str_t u1 = pj_str("offerer");
Benny Prijonofb9d9872007-03-21 22:05:58 +0000230 pj_str_t p1 = pj_str("pass1");
Benny Prijono2532b9d2007-03-22 01:16:37 +0000231 pj_str_t u2 = pj_str("answerer");
Benny Prijonofb9d9872007-03-21 22:05:58 +0000232 pj_str_t p2 = pj_str("pass2");
233
234 pj_ice_set_credentials(im1->ice, &u1, &p1, &u2, &p2);
235 pj_ice_set_credentials(im2->ice, &u2, &p2, &u1, &p1);
236 }
237
238 /* Send offer to im2 */
239 status = set_remote_list(im1, im2);
240 if (status != PJ_SUCCESS)
241 return -30;
242
243 /* Send answer to im1 */
244 status = set_remote_list(im2, im1);
245 if (status != PJ_SUCCESS)
246 return -35;
247
Benny Prijono2532b9d2007-03-22 01:16:37 +0000248 /* Mark start time */
249 pj_get_timestamp(&t_start);
250
Benny Prijonofb9d9872007-03-21 22:05:58 +0000251 /* Both can start now */
252 status = pj_ice_start_check(im1->ice);
253 if (status != PJ_SUCCESS)
254 return -40;
255
Benny Prijono2532b9d2007-03-22 01:16:37 +0000256#if 1
Benny Prijonofb9d9872007-03-21 22:05:58 +0000257 status = pj_ice_start_check(im2->ice);
258 if (status != PJ_SUCCESS)
Benny Prijono2532b9d2007-03-22 01:16:37 +0000259 return -45;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000260#endif
261
Benny Prijonofd7f1472007-03-22 11:59:03 +0000262 /* Poll for wait_before_send msecs before we send the first data */
263 for (;;) {
264 pj_timestamp t_now;
265
266 handle_events(1);
267
268 pj_get_timestamp(&t_now);
269 if (pj_elapsed_msec(&t_start, &t_now) >= wait_before_send)
270 break;
271 }
272
273 /* Send data. It must be successful! */
274 data_from_offerer = pj_str("from offerer");
275 status = pj_ice_send_data(im1->ice, 1, data_from_offerer.ptr, data_from_offerer.slen);
276 if (status != PJ_SUCCESS)
277 return -47;
278
279 data_from_answerer = pj_str("from answerer");
280 status = pj_ice_send_data(im2->ice, 1, data_from_answerer.ptr, data_from_answerer.slen);
281 if (status != PJ_SUCCESS)
282 return -48;
283
284 /* Poll to allow data to be received */
285 for (;;) {
286 pj_timestamp t_now;
287 handle_events(1);
288 pj_get_timestamp(&t_now);
289 if (pj_elapsed_msec(&t_start, &t_now) >= (wait_before_send + 200))
290 break;
291 }
292
293
Benny Prijonofb9d9872007-03-21 22:05:58 +0000294 /* Just wait until both completes, or timed out */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000295 while (!id1->complete || !id2->complete) {
296 pj_timestamp t_now;
297
Benny Prijonofb9d9872007-03-21 22:05:58 +0000298 handle_events(1);
299
Benny Prijono2532b9d2007-03-22 01:16:37 +0000300 pj_get_timestamp(&t_now);
Benny Prijonofd7f1472007-03-22 11:59:03 +0000301 if (pj_elapsed_msec(&t_start, &t_now) >= max_total_time) {
Benny Prijono2532b9d2007-03-22 01:16:37 +0000302 PJ_LOG(3,(THIS_FILE, "....error: timed-out"));
303 return -50;
304 }
305 }
Benny Prijonofb9d9872007-03-21 22:05:58 +0000306
Benny Prijono2532b9d2007-03-22 01:16:37 +0000307 /* Mark end-time */
308 pj_get_timestamp(&t_end);
309
310 /* Check status */
311 if (id1->err_code != PJ_SUCCESS)
312 return -53;
313 if (id2->err_code != PJ_SUCCESS)
314 return -56;
315
316 /* Verify that offerer gets answerer's transport address */
317 rcand = im1->ice->clist.checks[im1->ice->comp[0].nominated_check_id].rcand;
318 if (pj_memcmp(&rcand->addr, &im2->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
319 PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
320 return -60;
321 }
322
323 /* And the other way around */
324 rcand = im2->ice->clist.checks[im2->ice->comp[0].nominated_check_id].rcand;
325 if (pj_memcmp(&rcand->addr, &im1->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
326 PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
327 return -70;
328 }
329
Benny Prijonofd7f1472007-03-22 11:59:03 +0000330 /* Check that data is received in offerer */
331 if (id1->rx_rtp_cnt != 1) {
332 PJ_LOG(3,(THIS_FILE, "....error: data not received in offerer"));
333 return -80;
334 }
335 if (pj_strcmp2(&data_from_answerer, id1->last_rx_rtp_data) != 0) {
336 PJ_LOG(3,(THIS_FILE, "....error: data mismatch in offerer"));
337 return -82;
338 }
339
340 /* And the same in answerer */
341 if (id2->rx_rtp_cnt != 1) {
342 PJ_LOG(3,(THIS_FILE, "....error: data not received in answerer"));
343 return -84;
344 }
345 if (pj_strcmp2(&data_from_offerer, id2->last_rx_rtp_data) != 0) {
346 PJ_LOG(3,(THIS_FILE, "....error: data mismatch in answerer"));
347 return -82;
348 }
349
350
Benny Prijono2532b9d2007-03-22 01:16:37 +0000351 /* Done */
352 PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec",
353 pj_elapsed_msec(&t_start, &t_end)));
354
355 /* Wait for some more time */
356 PJ_LOG(3,(THIS_FILE, ".....waiting.."));
357 for (;;) {
358 pj_timestamp t_now;
359
360 pj_get_timestamp(&t_now);
Benny Prijonofd7f1472007-03-22 11:59:03 +0000361 if (pj_elapsed_msec(&t_start, &t_now) > max_total_time)
Benny Prijono2532b9d2007-03-22 01:16:37 +0000362 break;
363
364 handle_events(1);
365 }
366
367
368 pj_icemt_destroy(im1);
369 pj_icemt_destroy(im2);
370 return 0;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000371}
372
373
374int ice_test(void)
375{
376 int rc = 0;
377 pj_pool_t *pool;
378 pj_ioqueue_t *ioqueue;
379 pj_timer_heap_t *timer_heap;
Benny Prijono2532b9d2007-03-22 01:16:37 +0000380 pj_ice_cand ocand[PJ_ICE_MAX_CAND];
381 pj_ice_cand acand[PJ_ICE_MAX_CAND];
382 pj_str_t s;
383
Benny Prijonofb9d9872007-03-21 22:05:58 +0000384 pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
385 pj_ioqueue_create(pool, 12, &ioqueue);
386 pj_timer_heap_create(pool, 100, &timer_heap);
387
388 pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
389
390 pj_log_set_level(5);
391
Benny Prijono2532b9d2007-03-22 01:16:37 +0000392 /* Basic create/destroy */
Benny Prijonofb9d9872007-03-21 22:05:58 +0000393 rc = ice_basic_create_destroy_test();
394 if (rc != 0)
395 goto on_return;
396
Benny Prijono2532b9d2007-03-22 01:16:37 +0000397 /* Direct communication */
Benny Prijonofd7f1472007-03-22 11:59:03 +0000398 rc = perform_ice_test("Direct connection", 500, 1000, 0, NULL, 0, NULL);
Benny Prijonofb9d9872007-03-21 22:05:58 +0000399 if (rc != 0)
400 goto on_return;
401
Benny Prijono2532b9d2007-03-22 01:16:37 +0000402 /* Direct communication with invalid address */
403 pj_bzero(ocand, sizeof(ocand));
404 pj_sockaddr_in_init(&ocand[0].addr.ipv4, pj_cstr(&s, "127.0.0.127"), 1234);
405 pj_sockaddr_in_init(&ocand[0].base_addr.ipv4, pj_cstr(&s, "127.0.0.128"), 1234);
406 ocand[0].comp_id = 1;
407 ocand[0].foundation = pj_str("H2");
408 ocand[0].type = PJ_ICE_CAND_TYPE_HOST;
409
Benny Prijonofd7f1472007-03-22 11:59:03 +0000410 rc = perform_ice_test("Direct connection with 1 invalid address", 500, 1000, 1, ocand, 0, NULL);
Benny Prijono2532b9d2007-03-22 01:16:37 +0000411 if (rc != 0)
412 goto on_return;
413
Benny Prijonofd7f1472007-03-22 11:59:03 +0000414 /* Direct communication with two components */
415 rc = perform_ice_test("Direct connection with two components", 500, 1000, 0, NULL, 0, NULL);
416 if (rc != 0)
417 goto on_return;
418
419
Benny Prijono2532b9d2007-03-22 01:16:37 +0000420
Benny Prijonofb9d9872007-03-21 22:05:58 +0000421on_return:
422 pj_log_set_level(3);
423 pj_ioqueue_destroy(stun_cfg.ioqueue);
424 pj_pool_release(pool);
425 return rc;
426}
427