diff options
Diffstat (limited to 'pjnath/src/pjnath/ice_session.c')
-rw-r--r-- | pjnath/src/pjnath/ice_session.c | 201 |
1 files changed, 131 insertions, 70 deletions
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 09617965..50c593bd 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -40,6 +40,7 @@ static const char *cand_type_names[] = }; /* String names for pj_ice_sess_check_state */ +#if PJ_LOG_MAX_LEVEL >= 4 static const char *check_state_name[] = { "Frozen", @@ -55,6 +56,7 @@ static const char *clist_state_name[] = "Running", "Completed" }; +#endif /* PJ_LOG_MAX_LEVEL >= 4 */ static const char *role_names[] = { @@ -62,6 +64,15 @@ static const char *role_names[] = "Controlling" }; +/* Default ICE session preferences, according to draft-ice */ +static pj_uint8_t cand_type_prefs[4] = +{ + 126, /**< PJ_ICE_HOST_PREF */ + 100, /**< PJ_ICE_SRFLX_PREF. */ + 110, /**< PJ_ICE_PRFLX_PREF */ + 0 /**< PJ_ICE_RELAYED_PREF */ +}; + #define CHECK_NAME_LEN 128 #define LOG4(expr) PJ_LOG(4,expr) #define LOG5(expr) PJ_LOG(4,expr) @@ -71,15 +82,14 @@ static const char *role_names[] = typedef struct stun_data { - pj_ice_sess *ice; - unsigned lcand_id; + pj_ice_sess *ice; pj_ice_sess_cand *lcand; } stun_data; typedef struct timer_data { - pj_ice_sess *ice; - pj_ice_sess_checklist *clist; + pj_ice_sess *ice; + pj_ice_sess_checklist *clist; } timer_data; @@ -131,11 +141,6 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, pj_pool_t *pool, int *data_type, pj_str_t *data); -static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg, - void *user_data, - const pj_str_t *realm, - const pj_str_t *username, - const pj_str_t *nonce); PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type) @@ -188,8 +193,6 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, { pj_pool_t *pool; pj_ice_sess *ice; - char tmp[64]; - pj_str_t s; unsigned i; pj_status_t status; @@ -204,6 +207,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, ice->role = role; ice->tie_breaker.u32.hi = pj_rand(); ice->tie_breaker.u32.lo = pj_rand(); + ice->prefs = cand_type_prefs; pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), name, ice); @@ -226,18 +230,18 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, } if (local_ufrag == NULL) { - pj_ansi_snprintf(tmp, sizeof(tmp), "%x%x", pj_rand(), pj_rand()); - s = pj_str(tmp); - local_ufrag = &s; + ice->rx_ufrag.ptr = pj_pool_alloc(ice->pool, 16); + pj_create_random_string(ice->rx_ufrag.ptr, 16); + } else { + pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); } - pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); if (local_passwd == NULL) { - pj_ansi_snprintf(tmp, sizeof(tmp), "%x%x", pj_rand(), pj_rand()); - s = pj_str(tmp); - local_passwd = &s; + ice->rx_pass.ptr = pj_pool_alloc(ice->pool, 16); + pj_create_random_string(ice->rx_pass.ptr, 16); + } else { + pj_strdup(ice->pool, &ice->rx_pass, local_passwd); } - pj_strdup(ice->pool, &ice->rx_pass, local_passwd); /* Done */ @@ -316,6 +320,20 @@ PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, } +/* + * Change type preference + */ +PJ_DEF(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, + const pj_uint8_t prefs[4]) +{ + PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL); + ice->prefs = pj_pool_calloc(ice->pool, PJ_ARRAY_SIZE(prefs), + sizeof(pj_uint8_t)); + pj_memcpy(ice->prefs, prefs, sizeof(prefs)); + return PJ_SUCCESS; +} + + /* Find component by ID */ static pj_ice_sess_comp *find_comp(const pj_ice_sess *ice, unsigned comp_id) { @@ -421,35 +439,12 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, } -static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg, - void *user_data, - const pj_str_t *realm, - const pj_str_t *username, - const pj_str_t *nonce) -{ - /* We don't use NONCE */ - PJ_UNUSED_ARG(msg); - PJ_UNUSED_ARG(user_data); - PJ_UNUSED_ARG(realm); - PJ_UNUSED_ARG(username); - PJ_UNUSED_ARG(nonce); - return PJ_TRUE; -} - - -static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type, +static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, + pj_ice_cand_type type, pj_uint32_t local_pref, pj_uint32_t comp_id) { - pj_uint32_t type_pref[] = - { - PJ_ICE_HOST_PREF, - PJ_ICE_SRFLX_PREF, - PJ_ICE_PRFLX_PREF, - PJ_ICE_RELAYED_PREF - }; - - return ((type_pref[type] & 0xFF) << 24) + + return ((ice->prefs[type] & 0xFF) << 24) + ((local_pref & 0xFFFF) << 8) + (((256 - comp_id) & 0xFF) << 0); } @@ -492,7 +487,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, lcand->comp_id = comp_id; lcand->type = type; pj_strdup(ice->pool, &lcand->foundation, foundation); - lcand->prio = CALC_CAND_PRIO(type, local_pref, lcand->comp_id); + lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); pj_memcpy(&lcand->addr, addr, addr_len); pj_memcpy(&lcand->base_addr, base_addr, addr_len); if (rel_addr) @@ -520,7 +515,6 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, /* Associate data with this STUN session */ sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); sd->ice = ice; - sd->lcand_id = GET_LCAND_ID(lcand); sd->lcand = lcand; pj_stun_session_set_user_data(lcand->stun_sess, sd); @@ -530,7 +524,6 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; - auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce; auth_cred.data.dyn_cred.user_data = lcand->stun_sess; pj_stun_session_set_credential(lcand->stun_sess, &auth_cred); @@ -562,6 +555,7 @@ on_error: /* Find default candidate ID for the component */ +#if 0 PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, unsigned comp_id, int *cand_id) @@ -629,6 +623,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, pj_assert(!"Should have a candidate by now"); return PJ_EBUG; } +#endif /* if 0 */ #ifndef MIN @@ -639,11 +634,16 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, # define MAX(a,b) (a > b ? a : b) #endif -static pj_uint64_t CALC_CHECK_PRIO(const pj_ice_sess *ice, - const pj_ice_sess_cand *lcand, - const pj_ice_sess_cand *rcand) +static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, + const pj_ice_sess_cand *lcand, + const pj_ice_sess_cand *rcand) { pj_uint32_t O, A; + pj_timestamp prio; + + /* Original formula: + * pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) + */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { O = lcand->prio; @@ -653,10 +653,26 @@ static pj_uint64_t CALC_CHECK_PRIO(const pj_ice_sess *ice, A = lcand->prio; } + /* return ((pj_uint64_t)1 << 32) * MIN(O, A) + (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0); + */ + + prio.u32.hi = MIN(O,A); + prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0); + + return prio; } + +PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1, + const pj_ice_sess_check *c2) +{ + return pj_cmp_timestamp(&c1->prio, &c2->prio); +} + + +#if PJ_LOG_MAX_LEVEL >= 4 static const char *dump_check(char *buffer, unsigned bufsize, const pj_ice_sess_checklist *clist, const pj_ice_sess_check *check) @@ -690,7 +706,6 @@ static const char *dump_check(char *buffer, unsigned bufsize, return buffer; } -#if PJ_LOG_MAX_LEVEL >= 4 static void dump_checklist(const char *title, const pj_ice_sess *ice, const pj_ice_sess_checklist *clist) { @@ -746,7 +761,7 @@ static void sort_checklist(pj_ice_sess_checklist *clist) for (i=0; i<clist->count-1; ++i) { unsigned j, highest = i; for (j=i+1; j<clist->count; ++j) { - if (clist->checks[j].prio > clist->checks[highest].prio) { + if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) { highest = j; } } @@ -827,15 +842,15 @@ static void prune_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) else ljaddr = &ljcand->addr; - if (sockaddr_cmp(liaddr, ljaddr) == SOCKADDR_EQUAL && - sockaddr_cmp(&ricand->addr, &rjcand->addr) == SOCKADDR_EQUAL) + if (ricand == rjcand && + sockaddr_cmp(liaddr, ljaddr) == SOCKADDR_EQUAL) { /* Found duplicate, remove it */ char buf[CHECK_NAME_LEN]; LOG5((ice->obj_name, "Check %s pruned", - dump_check(buf, sizeof(buf), &ice->clist, - &clist->checks[j]))); + dump_check(buf, sizeof(buf), &ice->clist, + &clist->checks[j]))); pj_array_erase(clist->checks, sizeof(clist->checks[0]), clist->count, j); @@ -928,7 +943,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, if (comp->valid_check == NULL) { comp->valid_check = check; } else { - if (comp->valid_check->prio < check->prio) + if (CMP_CHECK_PRIO(comp->valid_check, check) < 0) comp->valid_check = check; } } @@ -1154,8 +1169,10 @@ static pj_status_t perform_check(pj_ice_sess *ice, status = pj_stun_session_create_req(lcand->stun_sess, PJ_STUN_BINDING_REQUEST, &check->tdata); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pjnath_perror(ice->obj_name, "Error creating STUN request", status); return status; + } /* Attach data to be retrieved later when STUN request transaction * completes and on_stun_request_complete() callback is called. @@ -1167,7 +1184,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, check->tdata->user_data = (void*) rd; /* Add PRIORITY */ - prio = CALC_CAND_PRIO(PJ_ICE_CAND_TYPE_PRFLX, 65535, + prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, lcand->comp_id); pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_PRIORITY, prio); @@ -1189,6 +1206,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, sizeof(pj_sockaddr_in), check->tdata); if (status != PJ_SUCCESS) { check->tdata = NULL; + pjnath_perror(ice->obj_name, "Error sending STUN request", status); return status; } @@ -1289,38 +1307,79 @@ static void periodic_timer(pj_timer_heap_t *th, start_periodic_check(th, te); } + +/* Utility: find string in string array */ +const pj_str_t *find_str(const pj_str_t *strlist[], unsigned count, + const pj_str_t *str) +{ + unsigned i; + for (i=0; i<count; ++i) { + if (pj_strcmp(strlist[i], str)==0) + return strlist[i]; + } + return NULL; +} + /* Start ICE check */ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) { pj_ice_sess_checklist *clist; const pj_ice_sess_cand *cand0; - unsigned i; + const pj_str_t *flist[PJ_ICE_MAX_CAND]; + unsigned i, flist_cnt = 0; PJ_ASSERT_RETURN(ice, PJ_EINVAL); - /* Checklist must be created */ + + /* Checklist must have been created */ PJ_ASSERT_RETURN(ice->clist.count > 0, PJ_EINVALIDOP); LOG4((ice->obj_name, "Starting ICE check..")); + /* The agent examines the check list for the first media stream (a + * media stream is the first media stream when it is described by + * the first m-line in the SDP offer and answer). For that media + * stream, it: + * + * - Groups together all of the pairs with the same foundation, + * + * - For each group, sets the state of the pair with the lowest + * component ID to Waiting. If there is more than one such pair, + * the one with the highest priority is used. + */ + clist = &ice->clist; - /* Pickup the first pair and set the state to Waiting */ - clist->checks[0].state = PJ_ICE_SESS_CHECK_STATE_WAITING; - cand0 = clist->checks[0].lcand; + /* Pickup the first pair for component 1. */ + for (i=0; i<clist->count; ++i) { + if (clist->checks[0].lcand->comp_id == 1) + break; + } + if (i == clist->count) { + pj_assert(!"Unable to find checklist for component 1"); + return PJNATH_EICEINCOMPID; + } + + /* Set this check to WAITING */ + check_set_state(ice, &clist->checks[i], + PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); + cand0 = clist->checks[i].lcand; + flist[flist_cnt++] = &clist->checks[i].lcand->foundation; /* Find all of the other pairs in that check list with the same * component ID, but different foundations, and sets all of their * states to Waiting as well. */ - for (i=1; i<clist->count; ++i) { + for (++i; i<clist->count; ++i) { const pj_ice_sess_cand *cand1; cand1 = clist->checks[i].lcand; - if (cand0->comp_id == cand1->comp_id && - pj_strcmp(&cand0->foundation, &cand1->foundation)!=0) + if (cand1->comp_id==cand0->comp_id && + find_str(flist, flist_cnt, &cand1->foundation)==NULL) { - clist->checks[i].state = PJ_ICE_SESS_CHECK_STATE_WAITING; + check_set_state(ice, &clist->checks[i], + PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); + flist[flist_cnt++] = &cand1->foundation; } } @@ -1341,7 +1400,9 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, unsigned addr_len) { stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); - return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, sd->lcand_id, + pj_ice_sess *ice = sd->ice; + return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, + GET_LCAND_ID(sd->lcand), pkt, pkt_size, dst_addr, addr_len); } |