Bug 1006809 - update triggered check behavior to RFC 5245. r+bwc r=mjf

--HG--
extra : transplant_source : q%BDT%0A%29%7B%7C%A0%92%B1%AE%81%AA%EE%94%9B%A0b3%A7
This commit is contained in:
Nils Ohlmeier [:drno] 2015-11-27 23:50:14 -08:00
parent d7a175fd88
commit b912f1ee3a
10 changed files with 373 additions and 233 deletions

View File

@ -314,15 +314,44 @@ nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
return NS_ERROR_FAILURE;
}
nr_ice_cand_pair *p1;
nr_ice_cand_pair *p1, *p2;
out_pairs->clear();
TAILQ_FOREACH(p1, &peer_stream->check_list, entry) {
TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) {
MOZ_ASSERT(p1);
MOZ_ASSERT(p1->local);
MOZ_ASSERT(p1->remote);
NrIceCandidatePair pair;
p2 = TAILQ_FIRST(&peer_stream->check_list);
while (p2) {
if (p1 == p2) {
/* Don't compare with our self. */
p2=TAILQ_NEXT(p2, check_queue_entry);
continue;
}
if (strncmp(p1->codeword,p2->codeword,sizeof(p1->codeword))==0) {
/* In case of duplicate pairs we only report the one winning pair */
if (
((p2->remote->component && (p2->remote->component->active == p2)) &&
!(p1->remote->component && (p1->remote->component->active == p1))) ||
((p2->peer_nominated || p2->nominated) &&
!(p1->peer_nominated || p1->nominated)) ||
(p2->priority > p1->priority) ||
((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) &&
(p1->state != NR_ICE_PAIR_STATE_SUCCEEDED))
) {
/* p2 is a better pair. */
break;
}
}
p2=TAILQ_NEXT(p2, check_queue_entry);
}
if (p2) {
/* p2 points to a duplicate but better pair so skip this one */
continue;
}
switch (p1->state) {
case NR_ICE_PAIR_STATE_FROZEN:
pair.state = NrIceCandidatePair::State::STATE_FROZEN;

View File

@ -909,10 +909,15 @@ class IceTestPeer : public sigslot::has_slots<> {
DumpCandidatePair(pairs[p]);
return false;
} else if (priority == pairs[p].priority) {
std::cerr << "Duplicate priority in subseqent pairs:" << std::endl;
DumpCandidatePair(pairs[p-1]);
DumpCandidatePair(pairs[p]);
return false;
if (!IceCandidatePairCompare()(pairs[p], pairs[p-1]) &&
!IceCandidatePairCompare()(pairs[p-1], pairs[p])) {
std::cerr << "Ignoring identical pair from trigger check" << std::endl;
} else {
std::cerr << "Duplicate priority in subseqent pairs:" << std::endl;
DumpCandidatePair(pairs[p-1]);
DumpCandidatePair(pairs[p]);
return false;
}
}
priority = pairs[p].priority;
}
@ -2120,7 +2125,7 @@ TEST_F(IceConnectTest, TestLoopbackOnlySortOf) {
Init(true, false);
AddStream("first", 1);
SetCandidateFilter(IsLoopbackCandidate);
ASSERT_TRUE(Gather());
ASSERT_TRUE(Gather(kDefaultTimeout, false));
SetExpectedRemoteCandidateAddr("127.0.0.1");
Connect();
}

View File

@ -247,7 +247,7 @@ static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg)
else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED);
}
else{
else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) {
/* OK, this didn't correspond to a pair on the check list, but
it probably matches one of our candidates */
@ -394,32 +394,82 @@ int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
return(_status);
}
static int nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair *pair)
{
int r,_status;
nr_ice_cand_pair *copy;
if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, &copy))
ABORT(r);
/* Preserve nomination status */
copy->peer_nominated= pair->peer_nominated;
copy->nominated = pair->nominated;
r_log(LOG_ICE,LOG_INFO,"CAND-PAIR(%s): Adding pair to check list and trigger check queue: %s",pair->codeword,pair->as_string);
if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy))
ABORT(r);
nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy);
copy->triggered = 1;
nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING);
_status=0;
abort:
return(_status);
}
int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
{
int r,_status;
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string);
switch(pair->state){
case NR_ICE_PAIR_STATE_FROZEN:
nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
/* Fall through */
case NR_ICE_PAIR_STATE_WAITING:
/* Start the checks */
if(r=nr_ice_candidate_pair_start(pctx,pair))
ABORT(r);
break;
case NR_ICE_PAIR_STATE_IN_PROGRESS:
if(r=nr_stun_client_force_retransmit(pair->stun_client))
ABORT(r);
break;
default:
break;
if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) {
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword);
return(0);
} else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) {
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword);
return(0);
}
/* Activate the media stream if required */
if(pair->remote->stream->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FROZEN){
/* Do not run this logic more than once on a given pair */
if(!pair->triggered){
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string);
pair->triggered=1;
switch(pair->state){
case NR_ICE_PAIR_STATE_FAILED:
/* OK, there was a pair, it's just invalid: According to Section
* 7.2.1.4, we need to resurrect it */
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on failed pair, resurrecting: %s",pctx->label,pair->codeword,pair->as_string);
/* fall through */
case NR_ICE_PAIR_STATE_FROZEN:
nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
/* fall through even further */
case NR_ICE_PAIR_STATE_WAITING:
/* Append it additionally to the trigger check queue */
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): Inserting pair to trigger check queue: %s",pctx->label,pair->codeword,pair->as_string);
nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair);
break;
case NR_ICE_PAIR_STATE_IN_PROGRESS:
/* Instead of trying to maintain two stun contexts on the same pair,
* and handling heterogenous responses and error conditions, we instead
* create a second pair that is identical except that it has the
* |triggered| bit set. We also cancel the original pair, but it can
* still succeed on its own in the special waiting state. */
if(r=nr_ice_candidate_copy_for_triggered_check(pair))
ABORT(r);
nr_ice_candidate_pair_cancel(pair->pctx,pair,1);
break;
default:
/* all states are handled - a new/unknown state should not
* automatically enter the start_checks() below */
assert(0);
break;
}
/* Ensure that the timers are running to start checks on the topmost entry
* of the triggered check queue. */
if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream))
ABORT(r);
}
@ -429,12 +479,16 @@ int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_
return(_status);
}
int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair)
int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state)
{
if(pair->state != NR_ICE_PAIR_STATE_FAILED){
/* If it's already running we need to terminate the stun */
if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){
nr_stun_client_cancel(pair->stun_client);
if(move_to_wait_state) {
nr_stun_client_wait(pair->stun_client);
} else {
nr_stun_client_cancel(pair->stun_client);
}
}
nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED);
}
@ -506,7 +560,8 @@ int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pai
pair->state=state;
if(pair->state==NR_ICE_PAIR_STATE_FAILED){
if(pair->state==NR_ICE_PAIR_STATE_FAILED ||
pair->state==NR_ICE_PAIR_STATE_CANCELLED){
if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
ABORT(r);
}
@ -524,6 +579,17 @@ int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
}
int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
{
if(pair->triggered_check_queue_entry.tqe_next ||
pair->triggered_check_queue_entry.tqe_prev)
return(0);
TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry);
return(0);
}
int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
{
nr_ice_cand_pair *c1;
@ -531,13 +597,13 @@ int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *p
c1=TAILQ_FIRST(head);
while(c1){
if(c1->priority < pair->priority){
TAILQ_INSERT_BEFORE(c1,pair,entry);
TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry);
break;
}
c1=TAILQ_NEXT(c1,entry);
c1=TAILQ_NEXT(c1,check_queue_entry);
}
if(!c1) TAILQ_INSERT_TAIL(head,pair,entry);
if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry);
return(0);
}

View File

@ -56,6 +56,8 @@ struct nr_ice_cand_pair_ {
on this check */
UCHAR nominated; /* Is this nominated or not */
UCHAR triggered; /* Ignore further trigger check requests */
UINT8 priority; /* The priority for this pair */
nr_ice_candidate *local; /* The local candidate */
nr_ice_candidate *remote; /* The remote candidate */
@ -68,7 +70,8 @@ struct nr_ice_cand_pair_ {
void *restart_role_change_cb_timer;
void *restart_nominated_cb_timer;
TAILQ_ENTRY(nr_ice_cand_pair_) entry;
TAILQ_ENTRY(nr_ice_cand_pair_) check_queue_entry; /* the check list */
TAILQ_ENTRY(nr_ice_cand_pair_) triggered_check_queue_entry; /* the trigger check queue */
};
int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp);
@ -76,10 +79,11 @@ int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair
int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state);
int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out);
int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair);
int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state);
int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair);
int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg);
int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp);
void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair);

View File

@ -704,6 +704,57 @@ int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *co
return 0;
}
static int nr_ice_component_pair_matches_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_transport_addr *local_addr, nr_stun_server_request *req)
{
if(pair->remote->component->component_id!=comp->component_id)
return(0);
if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
return(0);
if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
return(0);
return(1);
}
static int nr_ice_component_handle_triggered_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_stun_server_request *req, int *error)
{
nr_stun_message *sreq=req->request;
int r=0,_status;
if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){
if(comp->stream->pctx->controlling){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword);
}
else{
/* If this is the first time we've noticed this is nominated...*/
pair->peer_nominated=1;
if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){
pair->nominated=1;
if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
}
}
}
/* Note: the RFC says to trigger first and then nominate. But in that case
* the canceled trigger pair would get nominated and the cloned trigger pair
* would not get the nomination status cloned with it.*/
if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
_status=0;
abort:
return(r);
}
/* Section 7.2.1 */
static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error)
{
@ -711,12 +762,8 @@ static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_tr
nr_ice_candidate *pcand=0;
nr_stun_message *sreq=req->request;
nr_stun_message_attribute *attr;
int component_id_matched;
int local_addr_matched;
int remote_addr_matched;
nr_ice_cand_pair *found_invalid=0;
int r=0,_status;
int new_pcand_created=0;
int found_valid=0;
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
@ -767,157 +814,84 @@ static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_tr
pair=TAILQ_FIRST(&comp->stream->check_list);
while(pair){
component_id_matched = 0;
local_addr_matched = 0;
remote_addr_matched = 0;
if(pair->remote->component->component_id!=comp->component_id)
goto next_pair;
component_id_matched = 1;
if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
goto next_pair;
local_addr_matched=1;
if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
goto next_pair;
remote_addr_matched = 1;
if(pair->state==NR_ICE_PAIR_STATE_FAILED){
found_invalid=pair;
goto next_pair;
}
if (local_addr_matched && remote_addr_matched){
/* Since triggered checks create duplicate pairs (in this implementation)
* we are willing to handle multiple matches here. */
if(nr_ice_component_pair_matches_check(comp, pair, local_addr, req)){
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
break; /* OK, this is a known pair */
}
next_pair:
pair=TAILQ_NEXT(pair,entry);
}
if(!pair){
if(!found_invalid){
/* First find our local component candidate */
nr_ice_candidate *cand;
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label);
cand=TAILQ_FIRST(&comp->local_component->candidates);
while(cand){
if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
break;
cand=TAILQ_NEXT(cand,entry_comp);
}
/* Well, this really shouldn't happen, but it's an error from the
other side, so we just throw an error and keep going */
if(!cand){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string);
*error=400;
ABORT(R_NOT_FOUND);
}
/* Try to find matching peer active tcp candidate */
pcand=TAILQ_FIRST(&comp->candidates);
while(pcand){
if(pcand->tcp_type == TCP_TYPE_ACTIVE) {
if(!nr_transport_addr_cmp(&pcand->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
break;
}
pcand=TAILQ_NEXT(pcand,entry_comp);
}
if (!pcand){
/* We now need to make a peer reflexive */
if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
new_pcand_created=1;
if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label);
*error=487;
ABORT(R_BAD_DATA);
}
pcand->priority=attr->u.priority;
}
pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand,
&pair)) {
*error=(r==R_NO_MEMORY)?500:400;
if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error))
ABORT(r);
}
nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
if(r=nr_ice_component_insert_pair(comp,pair)) {
*error=(r==R_NO_MEMORY)?500:400;
nr_ice_candidate_pair_destroy(&pair);
ABORT(r);
}
/* Do this last, since any call to ABORT will destroy pcand */
if (new_pcand_created){
TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp);
pcand=0;
}
}
else{
/* OK, there was a pair, it's just invalid: According to Section
7.2.1.4, we need to resurrect it
*/
if(found_invalid->state == NR_ICE_PAIR_STATE_FAILED){
pair=found_invalid;
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair, resurrecting: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_WAITING);
}
else{
/* This shouldn't happen */
r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair that was not in state FAILED; this should not happen: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
*error=500;
ABORT(R_BAD_DATA);
}
++found_valid;
}
pair=TAILQ_NEXT(pair,check_queue_entry);
}
/* OK, we've got a pair to work with. Turn it on */
assert(pair);
if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){
if(comp->stream->pctx->controlling){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword);
if(!found_valid){
/* There were no matching pairs, so we need to create a new peer
* reflexive candidate pair. */
if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label);
*error=400;
ABORT(R_BAD_DATA);
}
else{
/* If this is the first time we've noticed this is nominated...*/
pair->peer_nominated=1;
if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){
pair->nominated=1;
/* Find our local component candidate */
nr_ice_candidate *cand;
if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
}
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label);
cand=TAILQ_FIRST(&comp->local_component->candidates);
while(cand){
if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
break;
cand=TAILQ_NEXT(cand,entry_comp);
}
}
if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
/* Well, this really shouldn't happen, but it's an error from the
other side, so we just throw an error and keep going */
if(!cand){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string);
*error=400;
ABORT(R_NOT_FOUND);
}
/* Now make a peer reflexive (remote) candidate */
if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
pcand->priority=attr->u.priority;
pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
/* Finally, create the candidate pair, insert into the check list, and
* apply the incoming check to it. */
if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand,
&pair)) {
*error=(r==R_NO_MEMORY)?500:400;
ABORT(r);
}
nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
if(r=nr_ice_component_insert_pair(comp,pair)) {
*error=(r==R_NO_MEMORY)?500:400;
nr_ice_candidate_pair_destroy(&pair);
ABORT(r);
}
/* Do this last, since any call to ABORT will destroy pcand */
TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp);
pcand=0;
/* Finally start the trigger check if needed */
if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error))
ABORT(r);
}
_status=0;
abort:
if(_status){
if (new_pcand_created)
nr_ice_candidate_destroy(&pcand);
nr_ice_candidate_destroy(&pcand);
assert(*error != 0);
if(r!=R_NO_MEMORY) assert(*error != 500);
}
@ -1013,6 +987,14 @@ int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pco
continue;
if(pcand->addr.ip_version != lcand->addr.ip_version)
continue;
/* This prevents our ice_unittest from pairing a loopback with a host
* candidate. */
if(nr_transport_addr_is_loopback(&lcand->addr) &&
!nr_transport_addr_is_loopback(&pcand->addr))
continue;
if(!nr_transport_addr_is_loopback(&lcand->addr) &&
nr_transport_addr_is_loopback(&pcand->addr))
continue;
/*
Two modes, depending on |pair_all_remote|
@ -1124,15 +1106,11 @@ static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_c
int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
{
int r,_status;
int fire_cb=0;
nr_ice_cand_pair *p2;
if(!comp->nominated)
fire_cb=1;
/* Are we changing what the nominated pair is? */
if(comp->nominated){
if(comp->nominated->priority > pair->priority)
if(comp->nominated->priority >= pair->priority)
return(0);
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword);
}
@ -1146,19 +1124,33 @@ int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pa
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
/* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */
p2=TAILQ_FIRST(&comp->stream->trigger_check_queue);
while(p2){
if((p2 != pair) &&
(p2->remote->component->component_id == comp->component_id)) {
assert(p2->state == NR_ICE_PAIR_STATE_WAITING ||
p2->state == NR_ICE_PAIR_STATE_CANCELLED);
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s in trigger check queue because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0))
ABORT(r);
}
p2=TAILQ_NEXT(p2,triggered_check_queue_entry);
}
p2=TAILQ_FIRST(&comp->stream->check_list);
while(p2){
if((p2 != pair) &&
(p2->remote->component->component_id == comp->component_id) &&
((p2->state == NR_ICE_PAIR_STATE_FROZEN) ||
(p2->state == NR_ICE_PAIR_STATE_WAITING))) {
(p2->state == NR_ICE_PAIR_STATE_WAITING))) {
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2))
if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0))
ABORT(r);
}
p2=TAILQ_NEXT(p2,entry);
p2=TAILQ_NEXT(p2,check_queue_entry);
}
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
@ -1193,7 +1185,7 @@ static int nr_ice_component_have_all_pairs_failed(nr_ice_component *comp)
}
}
p2=TAILQ_NEXT(p2,entry);
p2=TAILQ_NEXT(p2,check_queue_entry);
}
return(1);
@ -1233,7 +1225,7 @@ int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp)
if (comp->component_id == pair->local->component_id)
ct++;
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
/* Make and fill the array */
@ -1246,7 +1238,7 @@ int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp)
if (comp->component_id == pair->local->component_id)
pairs[ct++]=pair;
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
if (pctx->handler) {

View File

@ -72,6 +72,7 @@ int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ic
}
TAILQ_INIT(&stream->check_list);
TAILQ_INIT(&stream->trigger_check_queue);
stream->component_ct=components;
stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
@ -101,8 +102,10 @@ int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp)
nr_ice_component_destroy(&c1);
}
TAILQ_FOREACH_SAFE(p1, &stream->check_list, entry, p2){
TAILQ_REMOVE(&stream->check_list,p1,entry);
/* Note: all the entries from the trigger check queue are held in here as
* well, so we only clean up the super set. */
TAILQ_FOREACH_SAFE(p1, &stream->check_list, check_queue_entry, p2){
TAILQ_REMOVE(&stream->check_list,p1,check_queue_entry);
nr_ice_candidate_pair_destroy(&p1);
}
@ -308,46 +311,66 @@ int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ic
return(_status);
}
/* S 5.8 -- run the highest priority WAITING pair or if not available
FROZEN pair */
/* S 5.8 -- run the first pair from the triggered check queue (even after
* checks have completed S 8.1.2) or run the highest priority WAITING pair or
* if not available FROZEN pair from the check queue */
static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)
{
int r,_status;
nr_ice_media_stream *stream=cb_arg;
nr_ice_cand_pair *pair;
nr_ice_cand_pair *pair = 0;
int timer_val;
int timer_multiplier;
assert(stream->pctx->active_streams!=0);
timer_val=stream->pctx->ctx->Ta*stream->pctx->active_streams;
timer_multiplier=stream->pctx->active_streams;
/* Once the checks are completed we don't have an active streams any more,
* but we still need to process triggered checks. */
if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): (bug) bogus state for stream %s",stream->pctx->label,stream->label);
assert(timer_multiplier==0);
timer_multiplier=1;
}
assert(stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED);
assert(timer_multiplier!=0);
timer_val=stream->pctx->ctx->Ta*timer_multiplier;
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label);
stream->timer=0;
/* Find the highest priority WAITING check and move it to RUNNING */
pair=TAILQ_FIRST(&stream->check_list);
/* The trigger check queue has the highest priority */
pair=TAILQ_FIRST(&stream->trigger_check_queue);
while(pair){
if(pair->state==NR_ICE_PAIR_STATE_WAITING)
if(pair->state==NR_ICE_PAIR_STATE_WAITING){
/* Remove the pair from he trigger check queue */
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string);
TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry);
break;
pair=TAILQ_NEXT(pair,entry);
}
pair=TAILQ_NEXT(pair,triggered_check_queue_entry);
}
/* Hmmm... No WAITING. Let's look for FROZEN */
if(!pair){
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
if(pair->state==NR_ICE_PAIR_STATE_FROZEN){
if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
ABORT(r);
break;
if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
if(!pair){
/* Find the highest priority WAITING check and move it to RUNNING */
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
if(pair->state==NR_ICE_PAIR_STATE_WAITING)
break;
pair=TAILQ_NEXT(pair,check_queue_entry);
}
}
/* Hmmm... No WAITING. Let's look for FROZEN */
if(!pair){
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
if(pair->state==NR_ICE_PAIR_STATE_FROZEN){
if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
ABORT(r);
break;
}
pair=TAILQ_NEXT(pair,check_queue_entry);
}
pair=TAILQ_NEXT(pair,entry);
}
}
@ -364,20 +387,25 @@ static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)
return;
}
/* Start checks for this media stream (aka check list) */
int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
{
int r,_status;
/* Don't start the check timer if the stream is done (failed/completed) */
if (stream->ice_state > NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE) {
/* Don't start the check timer if the stream is failed */
if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
assert(0);
ABORT(R_INTERNAL);
}
if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE))
ABORT(r);
/* Even if the stream is completed already remote can still create a new
* triggered check request which needs to fire, but not change our stream
* state. */
if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) {
ABORT(r);
}
}
if (!stream->timer) {
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label);
@ -419,7 +447,7 @@ int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_strea
}
/* Already exists... fall through */
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
_status=0;
@ -442,7 +470,7 @@ static int nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream *stream,
ABORT(r);
unfroze++;
}
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
if(!unfroze)
@ -532,7 +560,7 @@ int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *s
while(pair){
nr_ice_candidate_pair_dump_state(pair,out);
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
return(0);
@ -625,10 +653,10 @@ int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_comp
/* OK, we need to cancel off everything on this component */
p2=TAILQ_FIRST(&stream->check_list);
while(p2){
if(r=nr_ice_candidate_pair_cancel(p2->pctx,p2))
if(r=nr_ice_candidate_pair_cancel(p2->pctx,p2,0))
ABORT(r);
p2=TAILQ_NEXT(p2,entry);
p2=TAILQ_NEXT(p2,check_queue_entry);
}
/* Cancel our timer */
@ -823,7 +851,7 @@ int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice
ABORT(r);
_status=0;
abort:
abort:
return(_status);
}
@ -846,7 +874,7 @@ int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int compo
comp->state = NR_ICE_COMPONENT_DISABLED;
_status=0;
abort:
abort:
return(_status);
}
@ -858,7 +886,7 @@ void nr_ice_media_stream_role_change(nr_ice_media_stream *stream)
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
nr_ice_candidate_pair_role_change(pair);
pair=TAILQ_NEXT(pair,entry);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
}

View File

@ -63,6 +63,7 @@ struct nr_ice_media_stream_ {
#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5
nr_ice_cand_pair_head check_list;
nr_ice_cand_pair_head trigger_check_queue;
void *timer; /* Check list periodic timer */
/* nr_ice_cand_pair_head valid_list; */

View File

@ -501,13 +501,15 @@ static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len,
sock->pending += len;
}
if (sock->pending && !already_armed) {
if (sock->pending) {
if (!already_armed) {
if ((r=nr_socket_buffered_stun_arm_writable_cb(sock)))
ABORT(r);
}
r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p)",
sock->remote_addr.as_string, (uint32_t)sock->pending,
already_armed ? "already" : "", &sock->pending);
}
r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p)",
sock->remote_addr.as_string, (uint32_t)sock->pending,
already_armed ? "already" : "", &sock->pending);
*written = original_len;

View File

@ -460,9 +460,8 @@ int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len
ATTACH_DATA(hmac_key, hmac_key_d);
if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED)
ABORT(R_REJECTED);
if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) &&
(ctx->state != NR_STUN_CLIENT_STATE_WAITING))
ABORT(R_REJECTED);
r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Inspecting STUN response (my_addr=%s, peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
@ -750,7 +749,8 @@ int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len
r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): Error processing response: %s, stun error code %d.", ctx->label, nr_strerror(_status), (int)ctx->error_code);
}
if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) {
if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) &&
(ctx->state != NR_STUN_CLIENT_STATE_WAITING)) {
/* Cancel the timer firing */
if (ctx->timer_handle) {
NR_async_timer_cancel(ctx->timer_handle);
@ -798,6 +798,17 @@ int nr_stun_client_cancel(nr_stun_client_ctx *ctx)
return(0);
}
int nr_stun_client_wait(nr_stun_client_ctx *ctx)
{
nr_stun_client_cancel(ctx);
ctx->state=NR_STUN_CLIENT_STATE_WAITING;
ctx->request_ct = ctx->maximum_transmits;
ctx->timeout_ms = ctx->maximum_transmits_timeout_ms;
NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle);
return(0);
}
int nr_stun_client_failed(nr_stun_client_ctx *ctx)
{

View File

@ -137,6 +137,7 @@ struct nr_stun_client_ctx_ {
#define NR_STUN_CLIENT_STATE_FAILED 3
#define NR_STUN_CLIENT_STATE_TIMED_OUT 4
#define NR_STUN_CLIENT_STATE_CANCELLED 5
#define NR_STUN_CLIENT_STATE_WAITING 6
int mode;
#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH 1
@ -191,6 +192,7 @@ int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp);
int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 mask);
int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr);
int nr_stun_client_cancel(nr_stun_client_ctx *ctx);
int nr_stun_client_wait(nr_stun_client_ctx *ctx);
int nr_stun_client_failed(nr_stun_client_ctx *ctx);
#endif