From c069895ffed343edf4ab1c40aba03d2e28f79b8a Mon Sep 17 00:00:00 2001 From: Richard Aas Date: Mon, 17 Jan 2011 06:06:23 +0000 Subject: [PATCH] ICE fixes --- include/re_ice.h | 16 ++++++ src/ice/candpair.c | 122 +++++++++++++++++++++++++++++++++++++-- src/ice/chklist.c | 32 ++++------- src/ice/comp.c | 138 +++++++++++++++++++++++++++++++++++---------- src/ice/connchk.c | 124 ++++++++++++++++++++++++++-------------- src/ice/gather.c | 2 +- src/ice/ice.c | 17 +++++- src/ice/ice.h | 25 +++++++- src/ice/icem.c | 71 +++++++++++++++++++++-- src/ice/stunsrv.c | 70 ++++++++++++++++------- src/ice/triggq.c | 7 +-- 11 files changed, 494 insertions(+), 130 deletions(-) diff --git a/include/re_ice.h b/include/re_ice.h index 9e1da06..fce4436 100644 --- a/include/re_ice.h +++ b/include/re_ice.h @@ -14,9 +14,22 @@ enum ice_compid { ICE_COMPID_RTCP = 2 }; +enum ice_nomination { + NOMINATION_REGULAR = 0, + NOMINATION_AGGRESSIVE +}; + struct ice; struct icem; +/** ICE Configuration */ +struct ice_conf { + enum ice_nomination nom; /**< Nomination algorithm */ + uint32_t rto; /**< STUN Retransmission TimeOut */ + uint32_t rc; /**< STUN Retransmission Count */ + bool debug; /**< Enable ICE debugging */ +}; + typedef void (ice_gather_h)(int err, uint16_t scode, const char *reason, void *arg); typedef void (ice_connchk_h)(int err, bool update, void *arg); @@ -24,6 +37,7 @@ typedef void (ice_connchk_h)(int err, bool update, void *arg); /* ICE Session */ int ice_alloc(struct ice **icep, enum ice_mode mode, bool offerer); +struct ice_conf *ice_conf(struct ice *ice); void ice_set_offerer(struct ice *ice, bool offerer); int ice_sdp_decode(struct ice *ice, const char *name, const char *value); int ice_conncheck_start(struct ice *ice); @@ -36,6 +50,7 @@ const char *ice_pwd(const struct ice *ice); /* ICE Media */ int icem_alloc(struct icem **icemp, struct ice *ice, int proto, int layer, ice_gather_h *gh, ice_connchk_h *chkh, void *arg); +void icem_set_name(struct icem *icem, const char *name); int icem_comp_add(struct icem *icem, uint8_t compid, void *sock); int icem_cand_add(struct icem *icem, uint8_t compid, uint16_t lprio, const char *ifname, const struct sa *addr); @@ -46,6 +61,7 @@ bool icem_verify_support(struct icem *icem, uint8_t compid, const struct sa *raddr); int icem_add_chan(struct icem *icem, uint8_t compid, const struct sa *raddr); bool icem_mismatch(const struct icem *icem); +void icem_update(struct icem *icem); int icem_sdp_decode(struct icem *icem, const char *name, const char *value); int icem_debug(struct re_printf *pf, const struct icem *icem); struct list *icem_lcandl(const struct icem *icem); diff --git a/src/ice/candpair.c b/src/ice/candpair.c index 056d418..2dcb630 100644 --- a/src/ice/candpair.c +++ b/src/ice/candpair.c @@ -16,7 +16,7 @@ #include "ice.h" -#define DEBUG_MODULE "candpair" +#define DEBUG_MODULE "cndpair" #define DEBUG_LEVEL 5 #include @@ -26,6 +26,7 @@ static void candpair_destructor(void *data) struct candpair *cp = data; list_unlink(&cp->le); + list_unlink(&cp->le_tq); mem_deref(cp->ct_conn); @@ -55,10 +56,15 @@ int icem_candpair_alloc(struct candpair **cpp, struct icem *icem, struct cand *lcand, struct cand *rcand) { struct candpair *cp; + struct icem_comp *comp; if (!icem || !lcand || !rcand) return EINVAL; + comp = icem_comp_find(icem, lcand->compid); + if (!comp) + return ENOENT; + cp = mem_zalloc(sizeof(*cp), candpair_destructor); if (!cp) return ENOMEM; @@ -66,10 +72,12 @@ int icem_candpair_alloc(struct candpair **cpp, struct icem *icem, list_append(&icem->checkl, &cp->le, cp); cp->icem = icem; + cp->comp = comp; cp->lcand = mem_ref(lcand); cp->rcand = mem_ref(rcand); cp->state = CANDPAIR_FROZEN; cp->rtt = -1; + cp->def = comp->def_lcand == lcand && comp->def_rcand == rcand; candpair_set_pprio(cp); @@ -120,6 +128,88 @@ void icem_candpair_cancel(struct candpair *cp) return; cp->ct_conn = mem_deref(cp->ct_conn); + + icem_conncheck_continue(cp->icem); +} + + +void icem_candpair_make_valid(struct candpair *cp) +{ + if (!cp) + return; + + cp->err = 0; + cp->scode = 0; + cp->valid = true; + + if (cp->tick_sent) + cp->rtt = (int)(tmr_jiffies() - cp->tick_sent); + + icem_candpair_set_state(cp, CANDPAIR_SUCCEEDED); + icem_candpair_move(cp, &cp->icem->validl); +} + + +void icem_candpair_failed(struct candpair *cp, int err, uint16_t scode) +{ + if (!cp) + return; + + cp->err = err; + cp->scode = scode; + + icem_candpair_set_state(cp, CANDPAIR_FAILED); +} + + +void icem_candpair_set_state(struct candpair *cp, enum candpair_state state) +{ + if (!cp) + return; + + if (cp->state != state) { + icecomp_printf(cp->comp, "FSM: %10s ===> %-10s\n", + ice_candpair_state2name(cp->state), + ice_candpair_state2name(state)); + } + + cp->state = state; +} + + +/** + * Delete all Candidate-Pairs where the Local candidate is of a given type + */ +void icem_candpairs_flush(struct list *lst, enum cand_type type, uint8_t id) +{ + struct le *le = list_head(lst); + + while (le) { + + struct candpair *cp = le->data; + + le = le->next; + + if (cp->lcand->compid != id) + continue; + + if (cp->lcand->type != type) + continue; + + /* also remove the local candidate */ + mem_deref(cp->lcand); + + mem_deref(cp); + } +} + + +bool icem_candpair_iscompleted(const struct candpair *cp) +{ + if (!cp) + return false; + + return cp->state == CANDPAIR_FAILED || cp->state == CANDPAIR_SUCCEEDED; } @@ -193,6 +283,25 @@ struct candpair *icem_candpair_find_st(const struct list *lst, uint8_t compid, } +struct candpair *icem_candpair_find_compid(const struct list *lst, + uint8_t compid) +{ + struct le *le; + + for (le = list_head(lst); le; le = le->next) { + + struct candpair *cp = le->data; + + if (cp->lcand->compid != compid) + continue; + + return cp; + } + + return NULL; +} + + bool icem_candpair_cmp_fnd(const struct candpair *cp1, const struct candpair *cp2) { @@ -211,7 +320,7 @@ int icem_candpair_debug(struct re_printf *pf, const struct candpair *cp) if (!cp) return 0; - err = re_hprintf(pf, "{%u} %10s {%c%c%c%c} %28H --> %28H", + err = re_hprintf(pf, "{%u} %10s {%c%c%c%c} %28H <---> %28H", cp->lcand->compid, ice_candpair_state2name(cp->state), cp->def ? 'D' : ' ', @@ -221,9 +330,14 @@ int icem_candpair_debug(struct re_printf *pf, const struct candpair *cp) icem_cand_print, cp->lcand, icem_cand_print, cp->rcand); - if (cp->rtt != -1) { + if (cp->rtt != -1) err |= re_hprintf(pf, " RTT=%dms", cp->rtt); - } + + if (cp->err) + err |= re_hprintf(pf, " (%s)", strerror(cp->err)); + + if (cp->scode) + err |= re_hprintf(pf, " [%u]", cp->scode); return err; } diff --git a/src/ice/chklist.c b/src/ice/chklist.c index 49db492..8239350 100644 --- a/src/ice/chklist.c +++ b/src/ice/chklist.c @@ -16,7 +16,7 @@ #include "ice.h" -#define DEBUG_MODULE "checklist" +#define DEBUG_MODULE "chklist" #define DEBUG_LEVEL 5 #include @@ -118,7 +118,7 @@ static void candpair_set_states(struct icem *icem) cp = cp2; } - cp->state = CANDPAIR_WAITING; + icem_candpair_set_state(cp, CANDPAIR_WAITING); } } @@ -169,7 +169,7 @@ int icem_checklist_form(struct icem *icem) /* If all of the pairs in the check list are now either in the Failed or Succeeded state: */ -static bool iscompleted(struct icem *icem) +static bool iscompleted(const struct icem *icem) { struct le *le; @@ -177,15 +177,8 @@ static bool iscompleted(struct icem *icem) const struct candpair *cp = le->data; - switch (cp->state) { - - case CANDPAIR_FAILED: - case CANDPAIR_SUCCEEDED: - continue; - - default: + if (!icem_candpair_iscompleted(cp)) return false; - } } return true; @@ -221,7 +214,9 @@ static void concluding_ice(struct icem_comp *comp) } -/* 7.1.3.3. Check List and Timer State Updates */ +/** + * Check List and Timer State Updates + */ void icem_checklist_update(struct icem *icem) { struct le *le; @@ -231,15 +226,14 @@ void icem_checklist_update(struct icem *icem) return; /* - o If there is not a pair in the valid list for each component of the - media stream, the state of the check list is set to Failed. - */ + * If there is not a pair in the valid list for each component of the + * media stream, the state of the check list is set to Failed. + */ for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; - if (!icem_candpair_find_st(&icem->validl, comp->id, - CANDPAIR_SUCCEEDED)) { + if (!icem_candpair_find_compid(&icem->validl, comp->id)) { DEBUG_WARNING("no candidate pair for compid %u\n", comp->id); err = ENOENT; @@ -251,9 +245,7 @@ void icem_checklist_update(struct icem *icem) if (!comp->cp_sel) continue; - /* remove TURN client if not used */ - if (comp->cp_sel->lcand->type != CAND_TYPE_RELAY) - comp->turnc = mem_deref(comp->turnc); + icem_comp_keepalive(comp, true); } icem->state = err ? CHECKLIST_FAILED : CHECKLIST_COMPLETED; diff --git a/src/ice/comp.c b/src/ice/comp.c index d9b2431..37f0406 100644 --- a/src/ice/comp.c +++ b/src/ice/comp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,12 +19,34 @@ #include "ice.h" -#define DEBUG_MODULE "comp" +#define DEBUG_MODULE "icecomp" #define DEBUG_LEVEL 5 #include enum {COMPID_MIN = 1, COMPID_MAX = 255}; +enum {DEFAULT_KEEPALIVE = 15}; + + +#if 0 +/* for debugging */ +static bool helper_send_handler(int *err, struct sa *dst, + struct mbuf *mb, void *arg) +{ + struct icem_comp *comp = arg; + + (void)comp; + (void)err; + (void)dst; + (void)mb; + + re_printf("{id=%d} ......... UDP send %u bytes to %J via %s\n", + comp->id, mbuf_get_left(mb), dst, + (mb->pos && comp->turnc) ? "tunnel" : "socket"); + + return false; +} +#endif static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg) @@ -33,40 +56,29 @@ static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg) struct stun_msg *msg = NULL; struct stun_unknown_attr ua; const size_t start = mb->pos; - int err; #if 0 re_printf("{%d} UDP recv_helper: %u bytes from %J\n", comp->id, mbuf_get_left(mb), src); #endif - err = stun_msg_decode(&msg, mb, &ua); - if (err) + if (stun_msg_decode(&msg, mb, &ua)) return false; - if (STUN_METHOD_BINDING != stun_msg_method(msg)) { - DEBUG_NOTICE("ignore STUN message %s from %J\n", - stun_method_name(stun_msg_method(msg)), src); - goto out; + if (STUN_METHOD_BINDING == stun_msg_method(msg)) { + + switch (stun_msg_class(msg)) { + + case STUN_CLASS_REQUEST: + (void)icem_stund_recv(comp, src, msg, start); + break; + + default: + (void)stun_ctrans_recv(icem->stun, msg, &ua); + break; + } } - switch (stun_msg_class(msg)) { - - case STUN_CLASS_REQUEST: - err = icem_stund_recv(comp, src, msg, start); - break; - - case STUN_CLASS_SUCCESS_RESP: - case STUN_CLASS_ERROR_RESP: - (void)stun_ctrans_recv(icem->stun, msg, &ua); - break; - - default: - DEBUG_WARNING("udp_recv: ignore STUN msg from %J\n", src); - break; - } - - out: mem_deref(msg); return true; /* handled */ @@ -77,10 +89,13 @@ static void destructor(void *data) { struct icem_comp *comp = data; + tmr_cancel(&comp->tmr_ka); + mem_deref(comp->ct_gath); mem_deref(comp->turnc); mem_deref(comp->cp_sel); - mem_deref(comp->def_cand); + mem_deref(comp->def_lcand); + mem_deref(comp->def_rcand); mem_deref(comp->uh); mem_deref(comp->sock); } @@ -141,7 +156,8 @@ int icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id, comp->sock = mem_ref(sock); comp->icem = icem; - err = udp_register_helper(&comp->uh, sock, NULL, icem->layer, NULL, + err = udp_register_helper(&comp->uh, sock, NULL, icem->layer, + NULL, /*helper_send_handler*/ helper_recv_handler, comp); if (err) goto out; @@ -173,13 +189,30 @@ int icem_comp_set_default_cand(struct icem_comp *comp) if (!cand) return ENOENT; - mem_deref(comp->def_cand); - comp->def_cand = mem_ref(cand); + mem_deref(comp->def_lcand); + comp->def_lcand = mem_ref(cand); return 0; } +void icem_comp_set_default_rcand(struct icem_comp *comp, struct cand *rcand) +{ + if (!comp) + return; + + icecomp_printf(comp, "Set default remote candidate: %s:%J\n", + ice_cand_type2name(rcand->type), &rcand->addr); + + mem_deref(comp->def_rcand); + comp->def_rcand = mem_ref(rcand); + + if (comp->turnc) { + (void)turnc_add_chan(comp->turnc, &rcand->addr, NULL, NULL); + } +} + + void icem_comp_set_selected(struct icem_comp *comp, struct candpair *cp) { if (!comp) @@ -207,3 +240,50 @@ struct icem_comp *icem_comp_find(const struct icem *icem, uint8_t compid) return NULL; } + + +static void timeout(void *arg) +{ + struct icem_comp *comp = arg; + struct candpair *cp; + + tmr_start(&comp->tmr_ka, DEFAULT_KEEPALIVE * 1000 + rand_u16() % 1000, + timeout, comp); + + /* find selected candidate-pair */ + cp = comp->cp_sel; + if (!cp) + return; + + (void)stun_indication(comp->icem->proto, comp->sock, &cp->rcand->addr, + (cp->lcand->type == CAND_TYPE_RELAY) ? 4 : 0, + STUN_METHOD_BINDING, NULL, 0, true, 0); +} + + +void icem_comp_keepalive(struct icem_comp *comp, bool enable) +{ + if (!comp) + return; + + if (enable) { + tmr_start(&comp->tmr_ka, DEFAULT_KEEPALIVE * 1000, + timeout, comp); + } + else { + tmr_cancel(&comp->tmr_ka); + } +} + + +void icecomp_printf(struct icem_comp *comp, const char *fmt, ...) +{ + va_list ap; + + if (!comp || !comp->icem->ice->conf.debug) + return; + + va_start(ap, fmt); + (void)re_printf("{%s.%u} %v", comp->icem->name, comp->id, fmt, &ap); + va_end(ap); +} diff --git a/src/ice/connchk.c b/src/ice/connchk.c index 4fafb76..bb31ada 100644 --- a/src/ice/connchk.c +++ b/src/ice/connchk.c @@ -22,7 +22,24 @@ #include -static void pace_next(struct icem *icem); +#define ICE_CONNCHECK_MULTIPLE 1 + + +static void pace_next(struct icem *icem) +{ +#if 0 + re_printf("\n"); + re_printf("---> Pace next check: checklist=%u validlist=%u" + " triggq=%u\n", + list_count(&icem->checkl), + list_count(&icem->validl), + list_count(&icem->triggl)); +#endif + + icem_conncheck_schedule_check(icem); + + icem_checklist_update(icem); +} /** Constructing a Valid Pair */ @@ -35,7 +52,7 @@ static void construct_valid_pair(struct icem *icem, struct candpair *cp, int err; lcand = icem_cand_find(&icem->lcandl, cp->lcand->compid, mapped); - rcand = icem_cand_find(&icem->rcandl, cp->lcand->compid, dest); + rcand = icem_cand_find(&icem->rcandl, cp->rcand->compid, dest); if (!lcand) { DEBUG_WARNING("no such local candidate: %J\n", mapped); return; @@ -45,32 +62,41 @@ static void construct_valid_pair(struct icem *icem, struct candpair *cp, return; } - /* New candidate? */ + /* New candidate? -- implicit success */ if (lcand != cp->lcand || rcand != cp->rcand) { - /* note: could be optimized */ - cp->state = CANDPAIR_FAILED; + if (lcand != cp->lcand) { + icecomp_printf(cp->comp, + "New local candidate for mapped %J\n", + mapped); + } + if (rcand != cp->rcand) { + icecomp_printf(cp->comp, + "New remote candidate for dest %J\n", + dest); + } - if (icem_candpair_find(&icem->validl, lcand, rcand)) + /* The original candidate pair is set to 'Failed' because + * the implicitly discovered pair is 'better'. + * This happens for UAs behind NAT where the original + * pair is of type 'host' and the implicit pair is 'srflx' + */ + icem_candpair_failed(cp, EINTR, 0); + + if (icem_candpair_find(&icem->validl, lcand, rcand)) { + DEBUG_NOTICE("candpair already in VALID list\n"); return; + } err = icem_candpair_alloc(&cp2, icem, lcand, rcand); if (err) return; - cp2->valid = true; - cp2->rtt = (int)(tmr_jiffies() - cp->tick_sent); - cp2->state = CANDPAIR_SUCCEEDED; - - /* Add to VALID LIST */ - icem_candpair_move(cp2, &icem->validl); + icem_candpair_make_valid(cp2); } else { /* Add to VALID LIST, the pair that generated the check */ - cp->valid = true; - cp->rtt = (int)(tmr_jiffies() - cp->tick_sent); - cp->state = CANDPAIR_SUCCEEDED; - icem_candpair_move(cp, &icem->validl); + icem_candpair_make_valid(cp); } } @@ -109,13 +135,14 @@ static void stunc_resp_handler(int err, uint16_t scode, const char *reason, (void)reason; #if ICE_TRACE - DEBUG_NOTICE("{id=%u} rx %H <--- %H '%u %s' (%s)\n", cp->lcand->compid, - icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, - scode, reason, err ? strerror(err) : ""); + icecomp_printf(cp->comp, "Rx %H <--- %H '%u %s' (%s)\n", + icem_cand_print, cp->lcand, + icem_cand_print, cp->rcand, + scode, reason, err ? strerror(err) : ""); #endif if (err) { - cp->state = CANDPAIR_FAILED; + icem_candpair_failed(cp, err, scode); goto out; } @@ -124,7 +151,8 @@ static void stunc_resp_handler(int err, uint16_t scode, const char *reason, case 0: /* Success case */ attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { - cp->state = CANDPAIR_FAILED; + DEBUG_WARNING("no XOR-MAPPED-ADDR in response\n"); + icem_candpair_failed(cp, EBADMSG, 0); break; } @@ -133,12 +161,12 @@ static void stunc_resp_handler(int err, uint16_t scode, const char *reason, case 487: /* Role Conflict */ ice_switch_local_role(icem->ice); - cp->state = CANDPAIR_WAITING; + icem_candpair_set_state(cp, CANDPAIR_WAITING); icem_triggq_push(icem, cp); break; default: - cp->state = CANDPAIR_FAILED; + icem_candpair_failed(cp, err, scode); break; } @@ -164,12 +192,7 @@ static int send_req(struct candpair *cp) if (!comp) return ENOENT; -#if ICE_TRACE - DEBUG_NOTICE("{id=%u} tx %H ---> %H (%s) %s\n", lcand->compid, - icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, - ice_candpair_state2name(cp->state), - cp->use_cand ? "[USE]" : ""); -#endif + icem_candpair_set_state(cp, CANDPAIR_INPROGRESS); (void)re_snprintf(username_buf, sizeof(username_buf), "%s:%s", icem->rufrag, ice->lufrag); @@ -182,7 +205,7 @@ static int send_req(struct candpair *cp) case ROLE_CONTROLLING: ctrl_attr = STUN_ATTR_CONTROLLING; - if (cp->use_cand) + if (cp->use_cand || ice->conf.nom == NOMINATION_AGGRESSIVE) use_cand = 1; break; @@ -194,6 +217,13 @@ static int send_req(struct candpair *cp) return EINVAL; } +#if ICE_TRACE + icecomp_printf(cp->comp, "Tx %H ---> %H (%s) %s\n", + icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, + ice_candpair_state2name(cp->state), + use_cand ? "[USE]" : ""); +#endif + /* A connectivity check MUST utilize the STUN short term credential mechanism. */ @@ -204,6 +234,11 @@ static int send_req(struct candpair *cp) cp->tick_sent = tmr_jiffies(); + if (cp->ct_conn) { + DEBUG_WARNING("send_req: CONNCHECK already Pending!\n"); + return EBUSY; + } + switch (lcand->type) { case CAND_TYPE_RELAY: @@ -225,7 +260,7 @@ static int send_req(struct candpair *cp) STUN_METHOD_BINDING, (uint8_t *)icem->rpwd, str_len(icem->rpwd), true, stunc_resp_handler, cp, - 4, + 3 + use_cand, STUN_ATTR_USERNAME, username_buf, STUN_ATTR_PRIORITY, &prio_prflx, ctrl_attr, &ice->tiebrk, @@ -248,11 +283,9 @@ static void do_check(struct candpair *cp) err = send_req(cp); if (err) { - cp->state = CANDPAIR_FAILED; + icem_candpair_failed(cp, err, 0); return; } - - cp->state = CANDPAIR_INPROGRESS; } @@ -304,22 +337,16 @@ static void timeout(void *arg) { struct icem *icem = arg; +#if ICE_CONNCHECK_MULTIPLE if (icem->state == CHECKLIST_RUNNING) { tmr_start(&icem->tmr_pace, 100, timeout, icem); } +#endif pace_next(icem); } -static void pace_next(struct icem *icem) -{ - icem_conncheck_schedule_check(icem); - - icem_checklist_update(icem); -} - - /** * Scheduling Checks */ @@ -342,7 +369,20 @@ int icem_conncheck_start(struct icem *icem) icem->state = CHECKLIST_RUNNING; + DEBUG_NOTICE("starting connectivity checks with %u candidate pairs\n", + list_count(&icem->checkl)); +#if 0 + re_printf("%H\n", icem_debug, icem); +#endif + tmr_start(&icem->tmr_pace, 1, timeout, icem); return 0; } + + +void icem_conncheck_continue(struct icem *icem) +{ + if (!tmr_isrunning(&icem->tmr_pace)) + tmr_start(&icem->tmr_pace, 1, timeout, icem); +} diff --git a/src/ice/gather.c b/src/ice/gather.c index 26ae73f..3a827fc 100644 --- a/src/ice/gather.c +++ b/src/ice/gather.c @@ -64,7 +64,7 @@ static void stun_resp_handler(int err, uint16_t scode, const char *reason, --icem->nstun; if (err || scode > 0) { - DEBUG_WARNING("{%u} keepalive failed: %s\n", + DEBUG_WARNING("{%u} STUN Request failed: %s\n", comp->id, strerror(err)); goto out; } diff --git a/src/ice/ice.c b/src/ice/ice.c index e076025..5029f5e 100644 --- a/src/ice/ice.c +++ b/src/ice/ice.c @@ -19,11 +19,17 @@ /* * ICE Implementation as of RFC 5245 - * - * - only Regular nomination is supported */ +static const struct ice_conf conf_default = { + NOMINATION_REGULAR, + ICE_DEFAULT_RTO_RTP, + ICE_DEFAULT_RC, + false +}; + + /** Determining Role */ static void ice_determine_role(struct ice *ice, bool offerer) { @@ -60,6 +66,7 @@ int ice_alloc(struct ice **icep, enum ice_mode mode, bool offerer) list_init(&ice->ml); + ice->conf = conf_default; ice->lmode = mode; ice->tiebrk = rand_u64(); @@ -74,6 +81,12 @@ int ice_alloc(struct ice **icep, enum ice_mode mode, bool offerer) } +struct ice_conf *ice_conf(struct ice *ice) +{ + return ice ? &ice->conf : NULL; +} + + void ice_set_offerer(struct ice *ice, bool offerer) { if (!ice) diff --git a/src/ice/ice.h b/src/ice/ice.h index e42fb2f..755da85 100644 --- a/src/ice/ice.h +++ b/src/ice/ice.h @@ -63,21 +63,24 @@ struct ice { char lpwd[23]; /**< Local Password */ struct list ml; /**< Media list (struct icem) */ uint64_t tiebrk; /**< Tie-break value for roleconflict */ + struct ice_conf conf; /**< ICE Configuration */ }; /** Defines a media-stream component */ struct icem_comp { struct le le; /**< Linked-list element */ struct icem *icem; /**< Parent ICE media */ - struct cand *def_cand; /**< Default candidate */ + struct cand *def_lcand; /**< Default local candidate */ + struct cand *def_rcand; /**< Default remote candidate */ struct candpair *cp_sel; /**< Selected candidate-pair */ struct udp_helper *uh; /**< UDP helper */ void *sock; /**< Transport socket */ uint16_t lport; /**< Local port number */ uint8_t id; /**< Component ID */ bool concluded; /**< Concluded flag */ - struct turnc *turnc; /**< TURN Client */ - struct stun_ctrans *ct_gath; /**< STUN Transaction for gathering */ + struct turnc *turnc; /**< TURN Client */ + struct stun_ctrans *ct_gath; /**< STUN Transaction for gathering */ + struct tmr tmr_ka; /**< Keep-alive timer */ }; /** Defines an ICE media-stream */ @@ -103,6 +106,7 @@ struct icem { ice_gather_h *gh; /**< Gather handler */ ice_connchk_h *chkh; /**< Connectivity check handler */ void *arg; /**< Handler argument */ + char name[32]; /**< Name of the media stream */ }; /** Defines a candidate */ @@ -124,7 +128,9 @@ struct cand { /** Defines a candidate pair */ struct candpair { struct le le; /**< List element */ + struct le le_tq; /**< Triggered queue element */ struct icem *icem; /**< Pointer to parent ICE media */ + struct icem_comp *comp; /**< Pointer to media-stream component */ struct cand *lcand; /**< Local candidate */ struct cand *rcand; /**< Remote candidate */ bool def; /**< Default flag */ @@ -136,6 +142,8 @@ struct candpair { int rtt; /**< Estimated Round-Trip Time in [ms] */ bool use_cand; /**< Use-candidate flag */ struct stun_ctrans *ct_conn; /**< STUN Transaction for conncheck */ + int err; /**< Saved error code, if failed */ + uint16_t scode; /**< Saved STUN code, if failed */ }; @@ -160,6 +168,11 @@ int icem_candpair_alloc(struct candpair **cpp, struct icem *icem, void icem_candpair_prio_order(struct list *lst); void icem_candpair_move(struct candpair *cp, struct list *list); void icem_candpair_cancel(struct candpair *cp); +void icem_candpair_make_valid(struct candpair *cp); +void icem_candpair_failed(struct candpair *cp, int err, uint16_t scode); +void icem_candpair_set_state(struct candpair *cp, enum candpair_state state); +void icem_candpairs_flush(struct list *lst, enum cand_type type, uint8_t id); +bool icem_candpair_iscompleted(const struct candpair *cp); bool icem_candpair_cmp(const struct candpair *cp1, const struct candpair *cp2); bool icem_candpair_cmp_fnd(const struct candpair *cp1, const struct candpair *cp2); @@ -168,6 +181,8 @@ struct candpair *icem_candpair_find(const struct list *lst, const struct cand *rcand); struct candpair *icem_candpair_find_st(const struct list *lst, uint8_t compid, enum candpair_state state); +struct candpair *icem_candpair_find_compid(const struct list *lst, + uint8_t compid); int icem_candpair_debug(struct re_printf *pf, const struct candpair *cp); int icem_candpairs_debug(struct re_printf *pf, const struct list *list); @@ -190,13 +205,17 @@ void icem_checklist_update(struct icem *icem); int icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id, void *sock); int icem_comp_set_default_cand(struct icem_comp *comp); +void icem_comp_set_default_rcand(struct icem_comp *comp, struct cand *rcand); void icem_comp_set_selected(struct icem_comp *comp, struct candpair *cp); struct icem_comp *icem_comp_find(const struct icem *icem, uint8_t compid); +void icem_comp_keepalive(struct icem_comp *comp, bool enable); +void icecomp_printf(struct icem_comp *comp, const char *fmt, ...); /* conncheck */ int icem_conncheck_start(struct icem *icem); void icem_conncheck_schedule_check(struct icem *icem); +void icem_conncheck_continue(struct icem *icem); /* triggered check queue */ diff --git a/src/ice/icem.c b/src/ice/icem.c index e863199..1c07e19 100644 --- a/src/ice/icem.c +++ b/src/ice/icem.c @@ -80,8 +80,8 @@ int icem_alloc(struct icem **icemp, struct ice *ice, int proto, int layer, goto out; /* Update STUN Transport */ - stun_conf(icem->stun)->rto = ICE_DEFAULT_RTO_RTP; - stun_conf(icem->stun)->rc = ICE_DEFAULT_RC; + stun_conf(icem->stun)->rto = ice->conf.rto; + stun_conf(icem->stun)->rc = ice->conf.rc; } if (err) @@ -99,6 +99,15 @@ int icem_alloc(struct icem **icemp, struct ice *ice, int proto, int layer, } +void icem_set_name(struct icem *icem, const char *name) +{ + if (!icem) + return; + + str_ncpy(icem->name, name, sizeof(icem->name)); +} + + int icem_comp_add(struct icem *icem, uint8_t compid, void *sock) { struct icem_comp *comp; @@ -156,29 +165,42 @@ void icem_cand_redund_elim(struct icem *icem) const struct sa *icem_cand_default(struct icem *icem, uint8_t compid) { const struct icem_comp *comp = icem_comp_find(icem, compid); - if (!comp || !comp->def_cand) + if (!comp || !comp->def_lcand) return NULL; - return &comp->def_cand->addr; + return &comp->def_lcand->addr; } /** - * Verifying ICE Support + * Verifying ICE Support and set default remote candidate + * + * @param icem ICE Media + * @param compid Component ID + * @param raddr Address of default remote candidate + * + * @return True if ICE is supported, otherwise false */ bool icem_verify_support(struct icem *icem, uint8_t compid, const struct sa *raddr) { + struct cand *rcand; bool match; if (!icem) return false; - match = !!icem_cand_find(&icem->rcandl, compid, raddr); + rcand = icem_cand_find(&icem->rcandl, compid, raddr); + match = rcand != NULL; if (!match) icem->mismatch = true; + if (rcand) { + icem_comp_set_default_rcand(icem_comp_find(icem, compid), + rcand); + } + return match; } @@ -198,6 +220,41 @@ int icem_add_chan(struct icem *icem, uint8_t compid, const struct sa *raddr) } +static void purge_relayed(struct icem *icem, struct icem_comp *comp) +{ + icecomp_printf(comp, "purge local RELAY candidates\n"); + + /* + * Purge all Candidate-Pairs where the Local candidate + * is of type "Relay" + */ + icem_candpairs_flush(&icem->checkl, CAND_TYPE_RELAY, comp->id); + icem_candpairs_flush(&icem->validl, CAND_TYPE_RELAY, comp->id); + + comp->turnc = mem_deref(comp->turnc); +} + + +void icem_update(struct icem *icem) +{ + struct le *le; + + if (!icem) + return; + + for (le = icem->compl.head; le; le = le->next) { + + struct icem_comp *comp = le->data; + + /* remove TURN client if not used by local "Selected" */ + if (comp->cp_sel) { + if (comp->cp_sel->lcand->type != CAND_TYPE_RELAY) + purge_relayed(icem, comp); + } + } +} + + bool icem_mismatch(const struct icem *icem) { return icem ? icem->mismatch : true; @@ -212,6 +269,8 @@ int icem_debug(struct re_printf *pf, const struct icem *icem) if (!icem) return 0; + err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name); + err |= re_hprintf(pf, " Local Candidates: %H", icem_cands_debug, &icem->lcandl); err |= re_hprintf(pf, " Remote Candidates: %H", diff --git a/src/ice/stunsrv.c b/src/ice/stunsrv.c index 23473fc..c344aaa 100644 --- a/src/ice/stunsrv.c +++ b/src/ice/stunsrv.c @@ -41,7 +41,8 @@ static int learn_peer_reflexive(struct icem_comp *comp, const struct sa *src, if (icem_cand_find(&icem->rcandl, comp->id, src)) return 0; - DEBUG_NOTICE("**** Adding Peer-Reflexive candidate: %J\n", src); + DEBUG_NOTICE("{%d} Adding Peer-Reflexive remote candidate: %J\n", + comp->id, src); /* The foundation of the candidate is set to an arbitrary value, @@ -62,6 +63,7 @@ static void triggered_check(struct icem *icem, struct cand *lcand, struct cand *rcand) { struct candpair *cp; + int err; if (!lcand || !rcand) return; @@ -71,12 +73,20 @@ static void triggered_check(struct icem *icem, struct cand *lcand, switch (cp->state) { +#if 0 + /* TODO: I am not sure why we should cancel the + * pending Connectivity check here. this + * can lead to a deadlock situation where + * both agents are stuck on sending + * triggered checks on the same candidate pair + */ case CANDPAIR_INPROGRESS: icem_candpair_cancel(cp); /*@fallthrough@*/ +#endif case CANDPAIR_FAILED: - cp->state = CANDPAIR_WAITING; + icem_candpair_set_state(cp, CANDPAIR_WAITING); /*@fallthrough@*/ case CANDPAIR_FROZEN: @@ -85,9 +95,23 @@ static void triggered_check(struct icem *icem, struct cand *lcand, break; case CANDPAIR_SUCCEEDED: + default: break; } } + else { + err = icem_candpair_alloc(&cp, icem, lcand, rcand); + if (err) { + DEBUG_WARNING("failed to allocate candpair\n"); + return; + } + + icem_candpair_prio_order(&icem->checkl); + + icem_candpair_set_state(cp, CANDPAIR_WAITING); + + icem_triggq_push(icem, cp); + } } @@ -114,12 +138,10 @@ static struct candpair *lookup_candpair(struct icem *icem, static void handle_stun(struct ice *ice, struct icem *icem, struct icem_comp *comp, const struct sa *src, - uint32_t prio, bool use_cand) + uint32_t prio, bool use_cand, bool tunnel) { - struct cand *lcand = NULL; - struct cand *rcand = NULL; + struct cand *lcand = NULL, *rcand = NULL; struct candpair *cp = NULL; - int err; rcand = icem_cand_find(&icem->rcandl, comp->id, src); if (rcand) { @@ -133,15 +155,30 @@ static void handle_stun(struct ice *ice, struct icem *icem, } #if ICE_TRACE - DEBUG_NOTICE("{id=%u} Binding Request from %J (candpair=%s)\n", - comp->id, src, - cp ? ice_candpair_state2name(cp->state) : "n/a"); + icecomp_printf(comp, "Rx Binding Request from %J via %s" + " (candpair=%s) %s\n", + src, tunnel ? "Tunnel" : "Socket", + cp ? ice_candpair_state2name(cp->state) : "n/a", + use_cand ? "[USE]" : ""); +#else + (void)tunnel; #endif + /* 7.2.1.3. Learning Peer Reflexive Candidates */ + (void)learn_peer_reflexive(comp, src, prio); + + /* 7.2.1.4. Triggered Checks */ + if (ICE_MODE_FULL == ice->lmode) + triggered_check(icem, lcand, rcand); + + /* 7.2.1.5. Updating the Nominated Flag */ if (use_cand) { if (ice->lrole == ROLE_CONTROLLED) { if (cp && cp->state == CANDPAIR_SUCCEEDED) { - DEBUG_NOTICE("setting NOMINATED flag\n"); + DEBUG_NOTICE("{id=%d} setting NOMINATED" + " flag on candpair [%H]\n", + comp->id, + icem_candpair_debug, cp); cp->nominated = true; } } @@ -151,15 +188,10 @@ static void handle_stun(struct ice *ice, struct icem *icem, icem_candpair_cancel(cp); icem_comp_set_selected(comp, cp); } + + /* ICE should complete now .. */ + icem_checklist_update(icem); } - - /* Send TRIGGERED CHECK to peer if mode=full */ - if (ICE_MODE_FULL == ice->lmode) - triggered_check(icem, lcand, rcand); - - err = learn_peer_reflexive(comp, src, prio); - - /* 7.2.1.5. Updating the Nominated Flag */ } @@ -227,7 +259,7 @@ int icem_stund_recv(struct icem_comp *comp, const struct sa *src, if (attr) use_cand = true; - handle_stun(ice, icem, comp, src, prio_prflx, use_cand); + handle_stun(ice, icem, comp, src, prio_prflx, use_cand, presz > 0); return stun_reply(icem->proto, comp->sock, src, presz, req, (uint8_t *)ice->lpwd, strlen(ice->lpwd), true, 2, diff --git a/src/ice/triggq.c b/src/ice/triggq.c index de1ffd1..998522f 100644 --- a/src/ice/triggq.c +++ b/src/ice/triggq.c @@ -22,8 +22,8 @@ void icem_triggq_push(struct icem *icem, struct candpair *cp) if (!icem || !cp) return; - if (!list_contains(&icem->triggl, &cp->le)) - icem_candpair_move(cp, &icem->triggl); + if (!list_contains(&icem->triggl, &cp->le_tq)) + list_append(&icem->triggl, &cp->le_tq, cp); } @@ -38,8 +38,7 @@ struct candpair *icem_triggq_pop(struct icem *icem) if (!cp) return NULL; - /* Move candidate pair back to Check-List */ - icem_candpair_move(cp, &icem->checkl); + list_unlink(&cp->le_tq); return cp; }