James Kuszmaul | 871d071 | 2021-01-17 11:30:43 -0800 | [diff] [blame^] | 1 | /** |
| 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 | |
| 29 | static 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 | |
| 41 | static 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 | |
| 62 | static 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 */ |
| 75 | static 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 | |
| 91 | static 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 | |
| 101 | int 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 */ |
| 157 | static 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 | */ |
| 176 | static 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 */ |
| 188 | static 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 | |
| 199 | static 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 | */ |
| 242 | int 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 | |
| 456 | struct 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 | |
| 497 | struct 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 | |
| 522 | int 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 | |
| 563 | void *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 | |
| 614 | void 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 | } |