blob: a5ab080a2c53061d273929a42c7aff061670220f [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file lcand.c Local ICE Candidates
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_list.h>
12#include <re_tmr.h>
13#include <re_sa.h>
14#include <re_net.h>
15#include <re_sys.h>
16#include <re_stun.h>
17#include <re_udp.h>
18#include <re_tcp.h>
19#include <re_ice.h>
20#include <re_trice.h>
21#include "trice.h"
22
23
24#define DEBUG_MODULE "icelcand"
25#define DEBUG_LEVEL 5
26#include <re_dbg.h>
27
28
29static bool tcpconn_frame_handler(struct trice *icem,
30 struct tcp_conn *tc, struct sa *src,
31 struct mbuf *mb, void *arg)
32{
33 struct ice_lcand *lcand = arg;
34 (void)icem;
35
36 return lcand->recvh(lcand, IPPROTO_TCP, tc,
37 src, mb, lcand->arg);
38}
39
40
41static void tcp_conn_handler(const struct sa *peer, void *arg)
42{
43 struct ice_lcand *lcand = arg;
44 int err;
45
46#if 0
47 trice_printf(lcand->icem,
48 "[local=%H] incoming TCP-connect from %J\n",
49 trice_cand_print, lcand, peer);
50#endif
51
52 err = trice_conn_alloc(&lcand->icem->connl, lcand->icem,
53 lcand->attr.compid, false,
54 &lcand->attr.addr, peer, lcand->ts, lcand->layer,
55 tcpconn_frame_handler, lcand);
56 if (err) {
57 DEBUG_WARNING("ice_conn_alloc error (%m)\n", err);
58 }
59}
60
61
62static void lcand_destructor(void *arg)
63{
64 struct ice_lcand *cand = arg;
65
66 list_unlink(&cand->le);
67
68 mem_deref(cand->ts);
69 mem_deref(cand->uh);
70 mem_deref(cand->us);
71}
72
73
74/** Foundation is a hash of IP address and candidate type */
75static int compute_foundation(struct ice_lcand *cand,
76 const struct sa *addr, enum ice_cand_type type)
77{
78 uint32_t v;
79
80 v = sa_hash(addr, SA_ADDR);
81 v ^= type;
82
83 if (re_snprintf(cand->attr.foundation, sizeof(cand->attr.foundation),
84 "%08x", v) < 0)
85 return ENOMEM;
86
87 return 0;
88}
89
90
91static bool trice_lcand_recv_handler(struct ice_lcand *lcand,
92 int proto, void *sock, const struct sa *src,
93 struct mbuf *mb, void *arg)
94{
95 struct trice *icem = arg;
96
97 return trice_stun_process(icem, lcand, proto, sock, src, mb);
98}
99
100
101int trice_add_lcandidate(struct ice_lcand **candp,
102 struct trice *icem, struct list *lst,
103 unsigned compid, char *foundation, int proto,
104 uint32_t prio, const struct sa *addr,
105 const struct sa *base_addr,
106 enum ice_cand_type type,
107 const struct sa *rel_addr,
108 enum ice_tcptype tcptype)
109{
110 struct ice_lcand *cand;
111 int err = 0;
112
113 if (!lst || !compid || !proto || !addr)
114 return EINVAL;
115
116 cand = mem_zalloc(sizeof(*cand), lcand_destructor);
117 if (!cand)
118 return ENOMEM;
119
120 cand->attr.compid = compid;
121 if (foundation)
122 str_ncpy(cand->attr.foundation, foundation,
123 sizeof(cand->attr.foundation));
124 else
125 err = compute_foundation(cand, addr, type);
126 cand->attr.proto = proto;
127 cand->attr.prio = prio;
128 cand->attr.addr = *addr;
129 cand->attr.type = type;
130 cand->attr.tcptype = tcptype;
131 if (rel_addr)
132 cand->attr.rel_addr = *rel_addr;
133 if (err)
134 goto out;
135
136 cand->icem = icem;
137
138 cand->recvh = trice_lcand_recv_handler;
139 cand->arg = icem;
140
141 if (base_addr)
142 cand->base_addr = *base_addr;
143
144 list_append(lst, &cand->le, cand);
145
146 out:
147 if (err)
148 mem_deref(cand);
149 else if (candp)
150 *candp = cand;
151
152 return err;
153}
154
155
156/* this one is only for Send statistics on Local Candidate */
157static bool udp_helper_send_handler(int *err, struct sa *dst,
158 struct mbuf *mb, void *arg)
159{
160 struct ice_lcand *lcand = arg;
161 (void)err;
162 (void)dst;
163 (void)mb;
164
165 lcand->stats.n_tx += 1;
166
167 return false; /* NOT handled */
168}
169
170
171/*
172 * lcand: on which Local Candidate to receive the packet
173 *
174 * return TRUE if handled
175 */
176static bool udp_helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
177{
178 struct ice_lcand *lcand = arg;
179
180 lcand->stats.n_rx += 1;
181
182 return lcand->recvh(lcand, IPPROTO_UDP, lcand->us,
183 src, mb, lcand->arg);
184}
185
186
187/* The incoming data should not get here */
188static void dummy_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
189{
190 struct ice_lcand *lcand = arg;
191
192 DEBUG_NOTICE("@@@@ NO-ONE cared about this UDP packet? @@@@@"
193 " (%zu bytes from %J to %s.%J)\n",
194 mbuf_get_left(mb), src,
195 ice_cand_type2name(lcand->attr.type),
196 &lcand->attr.addr);
197}
198
199static int udp_listen_range(struct udp_sock **usp, const struct sa *ip,
200 uint16_t min_port, uint16_t max_port,
201 udp_recv_h *rh, void *arg)
202{
203 struct sa laddr;
204 int tries = 64;
205 int err = 0;
206
207 sa_cpy(&laddr, ip);
208
209 /* try hard */
210 while (tries--) {
211 struct udp_sock *us;
212 uint16_t port;
213
214 port = (min_port + (rand_u16() % (max_port - min_port)));
215
216 sa_set_port(&laddr, port);
217 err = udp_listen(&us, &laddr, rh, arg);
218 if (err)
219 continue;
220
221 /* OK */
222 if (usp)
223 *usp = us;
224 break;
225 }
226
227 return err;
228}
229
230
231/*
232 * you can call this at any time
233 *
234 * @param addr HOST: SA_ADDR portion is used
235 * non-HOST: SA_ADDR + SA_PORT portion is used
236 *
237 * @param base_addr Optional
238 * @param rel_addr Optional
239 *
240 * @param layer mandatory for HOST and RELAY candidates
241 */
242int trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,
243 unsigned compid, int proto,
244 uint32_t prio, const struct sa *addr,
245 const struct sa *base_addr,
246 enum ice_cand_type type, const struct sa *rel_addr,
247 enum ice_tcptype tcptype,
248 void *sock, int layer)
249{
250 struct ice_lcand *lcand;
251 int err = 0;
252
253 if (!icem || !compid || !proto || !addr)
254 return EINVAL;
255
256 if (!sa_isset(addr, SA_ADDR)) {
257 DEBUG_WARNING("lcand_add: SA_ADDR is not set\n");
258 return EINVAL;
259 }
260 if (type != ICE_CAND_TYPE_HOST) {
261 if (!sa_isset(addr, SA_PORT)) {
262 DEBUG_WARNING("lcand_add: %s: SA_PORT"
263 " must be set (%J)\n",
264 ice_cand_type2name(type), addr);
265 return EINVAL;
266 }
267 }
268
269 /* lookup candidate, replace if PRIO is higher */
270
271 /* TODO: dont look up TCP-ACTIVE types for now (port is zero) */
272 if (proto == IPPROTO_UDP) {
273
274 lcand = trice_lcand_find(icem, -1, compid, proto, addr);
275 if (lcand) {
276 trice_printf(icem,
277 "add_local[%s.%J] --"
278 " candidate already exists"
279 " (%H)\n",
280 ice_cand_type2name(type), addr,
281 trice_cand_print, lcand);
282
283 if (prio > lcand->attr.prio)
284 lcand = mem_deref(lcand);
285 else {
286 goto out;
287 }
288 }
289 }
290
291 err = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid, NULL,
292 proto, prio, addr, base_addr,
293 type, rel_addr, tcptype);
294 if (err)
295 return err;
296
297 if (type == ICE_CAND_TYPE_HOST) {
298
299 switch (proto) {
300
301 case IPPROTO_UDP:
302 if (sock) {
303 struct sa laddr;
304
305 lcand->us = mem_ref(sock);
306
307 err = udp_local_get(lcand->us,
308 &laddr);
309 if (err)
310 goto out;
311
312 lcand->attr.addr = *addr;
313 sa_set_port(&lcand->attr.addr,
314 sa_port(&laddr));
315 }
316 else {
317 if (icem->ports.min && icem->ports.max) {
318 err = udp_listen_range(
319 &lcand->us,
320 addr,
321 icem->ports.min,
322 icem->ports.max,
323 dummy_udp_recv,
324 lcand);
325 }
326 else {
327 err = udp_listen(&lcand->us, addr,
328 dummy_udp_recv,
329 lcand);
330 }
331 if (err)
332 goto out;
333
334 err = udp_local_get(lcand->us,
335 &lcand->attr.addr);
336 if (err)
337 goto out;
338 }
339
340 err = udp_register_helper(&lcand->uh, lcand->us,
341 layer,
342 udp_helper_send_handler,
343 udp_helper_recv_handler,
344 lcand);
345 if (err)
346 goto out;
347 break;
348
349 case IPPROTO_TCP:
350
351 /* TCP-transport has 3 variants:
352 active, passive, so */
353
354 if (lcand->attr.tcptype == ICE_TCP_ACTIVE) {
355
356 /* the port MUST be set to 9 (i.e., Discard) */
357 /*sa_set_port(&lcand->attr.addr, 9); */
358 }
359 else if (lcand->attr.tcptype == ICE_TCP_PASSIVE ||
360 lcand->attr.tcptype == ICE_TCP_SO) {
361
362 err = tcp_listen(&lcand->ts, addr,
363 tcp_conn_handler, lcand);
364 if (err)
365 goto out;
366 err = tcp_local_get(lcand->ts,
367 &lcand->attr.addr);
368 if (err)
369 goto out;
370 }
371 else {
372 err = EPROTONOSUPPORT;
373 goto out;
374 }
375 break;
376
377 default:
378 err = EPROTONOSUPPORT;
379 goto out;
380 }
381 }
382 else if (type == ICE_CAND_TYPE_RELAY) {
383
384 switch (proto) {
385
386 case IPPROTO_UDP:
387
388 if (sock) {
389 lcand->us = mem_ref(sock);
390 }
391 else {
392 err = udp_listen(&lcand->us, NULL,
393 dummy_udp_recv, lcand);
394 if (err)
395 goto out;
396 }
397
398 err = udp_register_helper(&lcand->uh, lcand->us,
399 layer,
400 udp_helper_send_handler,
401 udp_helper_recv_handler,
402 lcand);
403 if (err)
404 goto out;
405
406 break;
407
408 default:
409 err = EPROTONOSUPPORT;
410 goto out;
411 }
412 }
413 else if (type == ICE_CAND_TYPE_SRFLX ||
414 type == ICE_CAND_TYPE_PRFLX) {
415
416 /* Special case for SRFLX UDP candidates, if he has
417 * its own UDP-socket that can be used.
418 */
419 if (proto == IPPROTO_UDP && sock) {
420
421 lcand->us = mem_ref(sock);
422
423 err = udp_register_helper(&lcand->uh, lcand->us,
424 layer,
425 udp_helper_send_handler,
426 udp_helper_recv_handler,
427 lcand);
428 if (err)
429 goto out;
430 }
431 }
432
433 lcand->layer = layer;
434
435 if (icem->lrole != ICE_ROLE_UNKNOWN) {
436 /* pair this local-candidate with all existing
437 * remote-candidates */
438 err = trice_candpair_with_local(icem, lcand);
439 if (err)
440 goto out;
441
442 /* new pair -- refresh the checklist timer */
443 trice_checklist_refresh(icem);
444 }
445
446 out:
447 if (err)
448 mem_deref(lcand);
449 else if (lcandp)
450 *lcandp = lcand;
451
452 return err;
453}
454
455
456struct ice_lcand *trice_lcand_find(struct trice *icem,
457 enum ice_cand_type type,
458 unsigned compid, int proto,
459 const struct sa *addr)
460{
461 struct list *lst;
462 struct le *le;
463
464 if (!icem)
465 return NULL;
466
467 if (!proto) {
468 DEBUG_WARNING("find_candidate: invalid args\n");
469 return NULL;
470 }
471
472 lst = &icem->lcandl;
473
474 for (le = list_head(lst); le; le = le->next) {
475
476 struct ice_cand_attr *cand = le->data;
477
478 if (type != (enum ice_cand_type)-1 && type != cand->type)
479 continue;
480
481 if (compid && cand->compid != compid)
482 continue;
483
484 if (cand->proto != proto)
485 continue;
486
487 if (addr && !sa_cmp(&cand->addr, addr, SA_ALL))
488 continue;
489
490 return (void *)cand;
491 }
492
493 return NULL;
494}
495
496
497struct ice_lcand *trice_lcand_find2(const struct trice *icem,
498 enum ice_cand_type type, int af)
499{
500 struct le *le;
501
502 if (!icem)
503 return NULL;
504
505 for (le = list_head(&icem->lcandl); le; le = le->next) {
506
507 struct ice_cand_attr *cand = le->data;
508
509 if (cand->type != type)
510 continue;
511
512 if (af != sa_af(&cand->addr))
513 continue;
514
515 return (void *)cand;
516 }
517
518 return NULL;
519}
520
521
522int trice_lcands_debug(struct re_printf *pf, const struct list *lst)
523{
524 struct le *le;
525 int err;
526
527 err = re_hprintf(pf, " (%u)\n", list_count(lst));
528
529 for (le = list_head(lst); le && !err; le = le->next) {
530
531 const struct ice_lcand *cand = le->data;
532
533 err |= re_hprintf(pf, " {%u} [tx=%3zu, rx=%3zu] "
534 "fnd=%-8s prio=%08x ",
535 cand->attr.compid,
536 cand->stats.n_tx,
537 cand->stats.n_rx,
538 cand->attr.foundation,
539 cand->attr.prio);
540
541 if (str_isset(cand->ifname))
542 err |= re_hprintf(pf, "%s:", cand->ifname);
543
544 err |= re_hprintf(pf, "%24H", trice_cand_print, cand);
545
546 if (sa_isset(&cand->base_addr, SA_ADDR)) {
547 err |= re_hprintf(pf, " (base-addr = %J)",
548 &cand->base_addr);
549 }
550
551 if (sa_isset(&cand->attr.rel_addr, SA_ADDR)) {
552 err |= re_hprintf(pf, " (rel-addr = %J)",
553 &cand->attr.rel_addr);
554 }
555
556 err |= re_hprintf(pf, "\n");
557 }
558
559 return err;
560}
561
562
563void *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand)
564{
565 struct ice_lcand *base = NULL;
566
567 if (!icem || !lcand)
568 return NULL;
569
570 if (sa_isset(&lcand->base_addr, SA_ALL)) {
571
572 enum ice_cand_type base_type;
573
574 base_type = ice_cand_type_base(lcand->attr.type);
575
576 base = trice_lcand_find(icem,
577 base_type,
578 lcand->attr.compid,
579 lcand->attr.proto,
580 &lcand->base_addr);
581 }
582
583 /* note: original lcand has presedence, fallback to base-candidate */
584 switch (lcand->attr.type) {
585
586 case ICE_CAND_TYPE_HOST:
587 return lcand->us;
588
589 case ICE_CAND_TYPE_SRFLX:
590 case ICE_CAND_TYPE_PRFLX:
591 if (lcand->us)
592 return lcand->us;
593 else if (base && base->us)
594 return base->us;
595 else {
596 DEBUG_NOTICE("lcand_sock: no SOCK or BASE for "
597 " type '%s'\n",
598 ice_cand_type2name(lcand->attr.type));
599 return NULL;
600 }
601 break;
602
603 case ICE_CAND_TYPE_RELAY:
604 return lcand->us;
605
606 default:
607 return NULL;
608 }
609
610 return NULL;
611}
612
613
614void trice_lcand_recv_packet(struct ice_lcand *lcand,
615 const struct sa *src, struct mbuf *mb)
616{
617 struct sa addr;
618
619 if (!lcand || !src || !mb)
620 return;
621
622 addr = *src;
623
624 udp_helper_recv_handler(&addr, mb, lcand);
625}