blob: d9600fa014a75b59c5b5d477eebfc1717e4f1375 [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
32 char rx_rtp_data[32];
33 char rx_rtcp_data[32];
Benny Prijonofb9d9872007-03-21 22:05:58 +000034};
35
36static pj_stun_config stun_cfg;
37
38static void on_ice_complete(pj_icemt *icemt,
39 pj_status_t status)
40{
41 struct ice_data *id = (struct ice_data*) icemt->user_data;
42 id->complete = PJ_TRUE;
43 id->err_code = status;
Benny Prijono2532b9d2007-03-22 01:16:37 +000044 PJ_LOG(3,(THIS_FILE, " ICE %s complete %s", id->obj_name,
45 (status==PJ_SUCCESS ? "successfully" : "with failure")));
Benny Prijonofb9d9872007-03-21 22:05:58 +000046}
47
48
49static void on_rx_rtp(pj_icemt *icemt,
50 void *pkt, pj_size_t size,
51 const pj_sockaddr_t *src_addr,
52 unsigned src_addr_len)
53{
54 struct ice_data *id = (struct ice_data*) icemt->user_data;
Benny Prijono2532b9d2007-03-22 01:16:37 +000055
Benny Prijonofb9d9872007-03-21 22:05:58 +000056 id->rx_rtp_cnt++;
Benny Prijono2532b9d2007-03-22 01:16:37 +000057 pj_memcpy(id->rx_rtp_data, pkt, size);
58
59 PJ_UNUSED_ARG(src_addr);
60 PJ_UNUSED_ARG(src_addr_len);
Benny Prijonofb9d9872007-03-21 22:05:58 +000061}
62
63
64static void on_rx_rtcp(pj_icemt *icemt,
65 void *pkt, pj_size_t size,
66 const pj_sockaddr_t *src_addr,
67 unsigned src_addr_len)
68{
69 struct ice_data *id = (struct ice_data*) icemt->user_data;
Benny Prijono2532b9d2007-03-22 01:16:37 +000070
Benny Prijonofb9d9872007-03-21 22:05:58 +000071 id->rx_rtcp_cnt++;
Benny Prijono2532b9d2007-03-22 01:16:37 +000072 pj_memcpy(id->rx_rtcp_data, pkt, size);
73
74 PJ_UNUSED_ARG(src_addr);
75 PJ_UNUSED_ARG(src_addr_len);
Benny Prijonofb9d9872007-03-21 22:05:58 +000076}
77
78
79static void handle_events(unsigned msec_timeout)
80{
81 pj_time_val delay;
82
83 pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
84
85 delay.sec = 0;
86 delay.msec = msec_timeout;
87 pj_time_val_normalize(&delay);
88
89 pj_ioqueue_poll(stun_cfg.ioqueue, &delay);
90}
91
92
93/* Basic create and destroy test */
94static int ice_basic_create_destroy_test()
95{
96 pj_icemt *im;
97 pj_ice *ice;
98 pj_icemt_cb icemt_cb;
99 pj_status_t status;
100
101 PJ_LOG(3,(THIS_FILE, "...basic create/destroy"));
102
103 pj_bzero(&icemt_cb, sizeof(icemt_cb));
104 icemt_cb.on_ice_complete = &on_ice_complete;
105 icemt_cb.on_rx_rtcp = &on_rx_rtp;
106 icemt_cb.on_rx_rtcp = &on_rx_rtcp;
107
108 status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING,
109 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im);
110 if (status != PJ_SUCCESS)
111 return -10;
112
113 ice = im->ice;
114
115 pj_icemt_destroy(im);
116
117 return 0;
118}
119
120
121static pj_status_t set_remote_list(pj_icemt *src, pj_icemt *dst)
122{
123 unsigned i, count;
124 unsigned cand_id[PJ_ICE_MAX_CAND];
125 pj_ice_cand cand[PJ_ICE_MAX_CAND];
126 pj_status_t status;
127
128 count = PJ_ARRAY_SIZE(cand_id);
129 status = pj_ice_enum_cands(src->ice, &count, cand_id);
130 if (status != PJ_SUCCESS)
131 return status;
132
133 for (i=0; i<count; ++i) {
134 pj_ice_cand *p_cand;
135 status = pj_ice_get_cand(src->ice, cand_id[i], &p_cand);
136 if (status != PJ_SUCCESS)
137 return status;
138
139 pj_memcpy(&cand[i], p_cand, sizeof(pj_ice_cand));
140 }
141
142 status = pj_ice_create_check_list(dst->ice, count, cand);
143 return status;
144}
145
146
Benny Prijono2532b9d2007-03-22 01:16:37 +0000147/* Perform ICE test with the following parameters:
148 *
149 * - title: The title of the test
150 * - ocand_cnt,
151 * ocand Additional candidates to be added to offerer
152 * - acand_cnt,
153 * acand Additional candidates to be added to answerer
154 *
155 * The additional candidates are invalid candidates, meaning they
156 * won't be reachable by the agents. They are used to "confuse"
157 * ICE processing.
158 */
159static int perform_ice_test(const char *title,
160 unsigned ocand_cnt,
161 const pj_ice_cand ocand[],
162 unsigned acand_cnt,
163 const pj_ice_cand acand[])
Benny Prijonofb9d9872007-03-21 22:05:58 +0000164{
165 pj_icemt *im1, *im2;
166 pj_icemt_cb icemt_cb;
167 struct ice_data *id1, *id2;
Benny Prijono2532b9d2007-03-22 01:16:37 +0000168 pj_timestamp t_start, t_end;
169 pj_ice_cand *rcand;
170 unsigned i;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000171 pj_status_t status;
172
Benny Prijono2532b9d2007-03-22 01:16:37 +0000173 PJ_LOG(3,(THIS_FILE, "...%s", title));
Benny Prijonofb9d9872007-03-21 22:05:58 +0000174
175 pj_bzero(&icemt_cb, sizeof(icemt_cb));
176 icemt_cb.on_ice_complete = &on_ice_complete;
177 icemt_cb.on_rx_rtcp = &on_rx_rtp;
178 icemt_cb.on_rx_rtcp = &on_rx_rtcp;
179
180 /* Create first ICE */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000181 status = pj_icemt_create(&stun_cfg, "offerer", PJ_ICE_ROLE_CONTROLLING,
Benny Prijonofb9d9872007-03-21 22:05:58 +0000182 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im1);
183 if (status != PJ_SUCCESS)
184 return -20;
185
186 id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data);
Benny Prijono2532b9d2007-03-22 01:16:37 +0000187 id1->obj_name = "offerer";
Benny Prijonofb9d9872007-03-21 22:05:58 +0000188 im1->user_data = id1;
189
Benny Prijono2532b9d2007-03-22 01:16:37 +0000190 /* Add additional candidates */
191 for (i=0; i<ocand_cnt; ++i) {
192 status = pj_ice_add_cand(im1->ice, 1, ocand[i].type, 65535,
193 &ocand[i].foundation, &ocand[i].addr,
194 &ocand[i].base_addr, &ocand[i].srv_addr,
195 sizeof(pj_sockaddr_in), NULL);
196 if (status != PJ_SUCCESS)
197 return -22;
198 }
199
Benny Prijonofb9d9872007-03-21 22:05:58 +0000200 /* Create second ICE */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000201 status = pj_icemt_create(&stun_cfg, "answerer", PJ_ICE_ROLE_CONTROLLED,
Benny Prijonofb9d9872007-03-21 22:05:58 +0000202 &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im2);
203 if (status != PJ_SUCCESS)
204 return -25;
205
206 id2 = PJ_POOL_ZALLOC_T(im2->pool, struct ice_data);
Benny Prijono2532b9d2007-03-22 01:16:37 +0000207 id2->obj_name = "answerer";
Benny Prijonofb9d9872007-03-21 22:05:58 +0000208 im2->user_data = id2;
209
Benny Prijono2532b9d2007-03-22 01:16:37 +0000210 /* Add additional candidates */
211 for (i=0; i<acand_cnt; ++i) {
212 status = pj_ice_add_cand(im1->ice, 1, acand[i].type, 65535,
213 &acand[i].foundation, &acand[i].addr,
214 &acand[i].base_addr, &acand[i].srv_addr,
215 sizeof(pj_sockaddr_in), NULL);
216 if (status != PJ_SUCCESS)
217 return -22;
218 }
219
220 /* Set credentials */
Benny Prijonofb9d9872007-03-21 22:05:58 +0000221 {
Benny Prijono2532b9d2007-03-22 01:16:37 +0000222 pj_str_t u1 = pj_str("offerer");
Benny Prijonofb9d9872007-03-21 22:05:58 +0000223 pj_str_t p1 = pj_str("pass1");
Benny Prijono2532b9d2007-03-22 01:16:37 +0000224 pj_str_t u2 = pj_str("answerer");
Benny Prijonofb9d9872007-03-21 22:05:58 +0000225 pj_str_t p2 = pj_str("pass2");
226
227 pj_ice_set_credentials(im1->ice, &u1, &p1, &u2, &p2);
228 pj_ice_set_credentials(im2->ice, &u2, &p2, &u1, &p1);
229 }
230
231 /* Send offer to im2 */
232 status = set_remote_list(im1, im2);
233 if (status != PJ_SUCCESS)
234 return -30;
235
236 /* Send answer to im1 */
237 status = set_remote_list(im2, im1);
238 if (status != PJ_SUCCESS)
239 return -35;
240
Benny Prijono2532b9d2007-03-22 01:16:37 +0000241 /* Mark start time */
242 pj_get_timestamp(&t_start);
243
Benny Prijonofb9d9872007-03-21 22:05:58 +0000244 /* Both can start now */
245 status = pj_ice_start_check(im1->ice);
246 if (status != PJ_SUCCESS)
247 return -40;
248
Benny Prijono2532b9d2007-03-22 01:16:37 +0000249#if 1
Benny Prijonofb9d9872007-03-21 22:05:58 +0000250 status = pj_ice_start_check(im2->ice);
251 if (status != PJ_SUCCESS)
Benny Prijono2532b9d2007-03-22 01:16:37 +0000252 return -45;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000253#endif
254
255 /* Just wait until both completes, or timed out */
Benny Prijono2532b9d2007-03-22 01:16:37 +0000256 while (!id1->complete || !id2->complete) {
257 pj_timestamp t_now;
258
Benny Prijonofb9d9872007-03-21 22:05:58 +0000259 handle_events(1);
260
Benny Prijono2532b9d2007-03-22 01:16:37 +0000261 pj_get_timestamp(&t_now);
262 if (pj_elapsed_msec(&t_start, &t_now) >= 10000) {
263 PJ_LOG(3,(THIS_FILE, "....error: timed-out"));
264 return -50;
265 }
266 }
Benny Prijonofb9d9872007-03-21 22:05:58 +0000267
Benny Prijono2532b9d2007-03-22 01:16:37 +0000268 /* Mark end-time */
269 pj_get_timestamp(&t_end);
270
271 /* Check status */
272 if (id1->err_code != PJ_SUCCESS)
273 return -53;
274 if (id2->err_code != PJ_SUCCESS)
275 return -56;
276
277 /* Verify that offerer gets answerer's transport address */
278 rcand = im1->ice->clist.checks[im1->ice->comp[0].nominated_check_id].rcand;
279 if (pj_memcmp(&rcand->addr, &im2->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
280 PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
281 return -60;
282 }
283
284 /* And the other way around */
285 rcand = im2->ice->clist.checks[im2->ice->comp[0].nominated_check_id].rcand;
286 if (pj_memcmp(&rcand->addr, &im1->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
287 PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
288 return -70;
289 }
290
291 /* Done */
292 PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec",
293 pj_elapsed_msec(&t_start, &t_end)));
294
295 /* Wait for some more time */
296 PJ_LOG(3,(THIS_FILE, ".....waiting.."));
297 for (;;) {
298 pj_timestamp t_now;
299
300 pj_get_timestamp(&t_now);
301 if (pj_elapsed_msec(&t_end, &t_now) > 10000)
302 break;
303
304 handle_events(1);
305 }
306
307
308 pj_icemt_destroy(im1);
309 pj_icemt_destroy(im2);
310 return 0;
Benny Prijonofb9d9872007-03-21 22:05:58 +0000311}
312
313
314int ice_test(void)
315{
316 int rc = 0;
317 pj_pool_t *pool;
318 pj_ioqueue_t *ioqueue;
319 pj_timer_heap_t *timer_heap;
Benny Prijono2532b9d2007-03-22 01:16:37 +0000320 pj_ice_cand ocand[PJ_ICE_MAX_CAND];
321 pj_ice_cand acand[PJ_ICE_MAX_CAND];
322 pj_str_t s;
323
Benny Prijonofb9d9872007-03-21 22:05:58 +0000324 pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
325 pj_ioqueue_create(pool, 12, &ioqueue);
326 pj_timer_heap_create(pool, 100, &timer_heap);
327
328 pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
329
330 pj_log_set_level(5);
331
Benny Prijono2532b9d2007-03-22 01:16:37 +0000332 /* Basic create/destroy */
Benny Prijonofb9d9872007-03-21 22:05:58 +0000333 rc = ice_basic_create_destroy_test();
334 if (rc != 0)
335 goto on_return;
336
Benny Prijono2532b9d2007-03-22 01:16:37 +0000337 /* Direct communication */
338 rc = perform_ice_test("Direct connection", 0, NULL, 0, NULL);
Benny Prijonofb9d9872007-03-21 22:05:58 +0000339 if (rc != 0)
340 goto on_return;
341
Benny Prijono2532b9d2007-03-22 01:16:37 +0000342 /* Direct communication with invalid address */
343 pj_bzero(ocand, sizeof(ocand));
344 pj_sockaddr_in_init(&ocand[0].addr.ipv4, pj_cstr(&s, "127.0.0.127"), 1234);
345 pj_sockaddr_in_init(&ocand[0].base_addr.ipv4, pj_cstr(&s, "127.0.0.128"), 1234);
346 ocand[0].comp_id = 1;
347 ocand[0].foundation = pj_str("H2");
348 ocand[0].type = PJ_ICE_CAND_TYPE_HOST;
349
350 rc = perform_ice_test("Direct connection with 1 invalid address", 1, ocand, 0, NULL);
351 if (rc != 0)
352 goto on_return;
353
354
Benny Prijonofb9d9872007-03-21 22:05:58 +0000355on_return:
356 pj_log_set_level(3);
357 pj_ioqueue_destroy(stun_cfg.ioqueue);
358 pj_pool_release(pool);
359 return rc;
360}
361