ICE fixes

This commit is contained in:
Richard Aas 2011-01-17 06:06:23 +00:00
parent ec3863310f
commit c069895ffe
11 changed files with 494 additions and 130 deletions

View file

@ -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);

View file

@ -16,7 +16,7 @@
#include "ice.h"
#define DEBUG_MODULE "candpair"
#define DEBUG_MODULE "cndpair"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
@ -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;
}

View file

@ -16,7 +16,7 @@
#include "ice.h"
#define DEBUG_MODULE "checklist"
#define DEBUG_MODULE "chklist"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
@ -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;

View file

@ -10,6 +10,7 @@
#include <re_mbuf.h>
#include <re_list.h>
#include <re_tmr.h>
#include <re_sys.h>
#include <re_sa.h>
#include <re_udp.h>
#include <re_stun.h>
@ -18,12 +19,34 @@
#include "ice.h"
#define DEBUG_MODULE "comp"
#define DEBUG_MODULE "icecomp"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
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);
}

View file

@ -22,7 +22,24 @@
#include <re_dbg.h>
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);
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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 */

View file

@ -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",

View file

@ -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,

View file

@ -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;
}