/* also regenerates all_pairs_list */ static void __recalc_pair_prios(struct ice_agent *ag) { struct ice_candidate_pair *pair; GList *l; GQueue nominated, valid, succ, all; ilog(LOG_DEBUG, "Recalculating all ICE pair priorities"); g_tree_remove_all(&nominated, ag->nominated_pairs); g_tree_remove_all(&succ, ag->succeeded_pairs); g_tree_remove_all(&valid, ag->valid_pairs); g_tree_remove_all(&all, ag->all_pairs); for (l = ag->candidate_pairs.head; l; l = l->next) { pair = l->data; __do_ice_pair_priority(pair); /* this changes the packets, so we must keep these from being seen as retransmits */ __new_stun_transaction(pair); } g_tree_add_all(ag->nominated_pairs, &nominated); g_tree_add_all(ag->succeeded_pairs, &succ); g_tree_add_all(ag->valid_pairs, &valid); g_tree_add_all(ag->all_pairs, &all); __all_pairs_list(ag); }
/* agent must be locked */ static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, struct packet_stream *ps, struct sockaddr_in6 *src, struct interface_address *ifa, unsigned long priority) { struct ice_candidate *cand, *old_cand; struct ice_candidate_pair *pair; struct call *call = ag->call; cand = g_slice_alloc0(sizeof(*cand)); cand->component_id = ps->component; cand->transport = ITP_UDP; cand->priority = priority; cand->endpoint.ip46 = src->sin6_addr; cand->endpoint.port = ntohs(src->sin6_port); cand->type = ICT_PRFLX; __cand_ice_foundation(call, cand); old_cand = __foundation_lookup(ag, &cand->foundation, ps->component); if (old_cand && old_cand->priority > priority) { /* this is possible if two distinct requests are received from the same NAT IP * address, but from different ports. we cannot distinguish such candidates and * will drop the one with the lower priority */ g_slice_free1(sizeof(*cand), cand); pair = __pair_lookup(ag, old_cand, ifa); if (pair) goto out; /* nothing to do */ cand = old_cand; goto pair; } g_queue_push_tail(&ag->remote_candidates, cand); g_hash_table_insert(ag->candidate_hash, cand, cand); g_hash_table_insert(ag->foundation_hash, cand, cand); pair: pair = __pair_candidate(ifa, ag, cand, ps); PAIR_SET(pair, LEARNED); __all_pairs_list(ag); out: return pair; }
/* agent must be locked */ static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, struct stream_fd *sfd, const endpoint_t *src, unsigned long priority) { struct ice_candidate *cand, *old_cand; struct ice_candidate_pair *pair; struct call *call = ag->call; struct packet_stream *ps = sfd->stream; cand = g_slice_alloc0(sizeof(*cand)); cand->component_id = ps->component; cand->transport = sfd->local_intf->spec->local_address.type; // XXX add socket type into socket_t? cand->priority = priority; cand->endpoint = *src; cand->type = ICT_PRFLX; __cand_ice_foundation(call, cand); old_cand = __foundation_lookup(ag, &cand->foundation, ps->component); if (old_cand && old_cand->priority > priority) { /* this is possible if two distinct requests are received from the same NAT IP * address, but from different ports. we cannot distinguish such candidates and * will drop the one with the lower priority */ g_slice_free1(sizeof(*cand), cand); pair = __pair_lookup(ag, old_cand, sfd->local_intf); if (pair) goto out; /* nothing to do */ cand = old_cand; goto pair; } g_queue_push_tail(&ag->remote_candidates, cand); g_hash_table_insert(ag->candidate_hash, cand, cand); g_hash_table_insert(ag->foundation_hash, cand, cand); pair: pair = __pair_candidate(sfd, ag, cand); PAIR_SET(pair, LEARNED); __all_pairs_list(ag); out: return pair; }
/* called with the call lock held in W, hence agent doesn't need to be locked */ void ice_update(struct ice_agent *ag, struct stream_params *sp) { GList *l, *k; struct ice_candidate *cand, *dup; struct call_media *media; struct call *call; int recalc = 0; unsigned int comps; struct packet_stream *components[MAX_COMPONENTS], *ps; GQueue *candidates; if (!ag) return; atomic64_set(&ag->last_activity, poller_now); media = ag->media; call = media->call; __role_change(ag, MEDIA_ISSET(media, ICE_CONTROLLING)); if (sp) { /* check for ICE restarts */ if (ag->ufrag[0].s && sp->ice_ufrag.s && str_cmp_str(&ag->ufrag[0], &sp->ice_ufrag)) __ice_restart(ag); else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) __ice_restart(ag); else if (ag->local_interface != media->interface) __ice_restart(ag); /* update remote info */ if (sp->ice_ufrag.s) call_str_cpy(call, &ag->ufrag[0], &sp->ice_ufrag); if (sp->ice_pwd.s) call_str_cpy(call, &ag->pwd[0], &sp->ice_pwd); candidates = &sp->ice_candidates; } else /* this is a dummy update in case rtcp-mux has changed */ candidates = &ag->remote_candidates; /* get our component streams */ ZERO(components); comps = 0; for (l = media->streams.head; l; l = l->next) components[comps++] = l->data; if (comps == 2 && MEDIA_ISSET(media, RTCP_MUX)) components[1] = NULL; comps = 0; for (l = candidates->head; l; l = l->next) { if (ag->remote_candidates.length >= MAX_ICE_CANDIDATES) { ilog(LOG_WARNING, "Maxmimum number of ICE candidates exceeded"); break; } cand = l->data; /* skip invalid */ if (!cand->component_id || cand->component_id > G_N_ELEMENTS(components)) continue; ps = components[cand->component_id - 1]; if (ps) /* only count active components */ comps = MAX(comps, cand->component_id); dup = g_hash_table_lookup(ag->candidate_hash, cand); if (!sp && dup) /* this isn't a real update, so only check pairings */ goto pair; /* check for duplicates */ if (dup) { /* if this is peer reflexive, we've learned it through STUN. * otherwise it's simply one we've seen before. */ if (dup->type == ICT_PRFLX) { ilog(LOG_DEBUG, "Replacing previously learned prflx ICE candidate with " STR_FORMAT":%lu", STR_FMT(&cand->foundation), cand->component_id); } else { /* if the new one has higher priority then the old one, then we * update it, otherwise we just drop it */ if (cand->priority <= dup->priority) { ilog(LOG_DEBUG, "Dropping new ICE candidate "STR_FORMAT" in favour of " STR_FORMAT":%lu", STR_FMT(&cand->foundation), STR_FMT(&dup->foundation), cand->component_id); continue; } ilog(LOG_DEBUG, "Replacing known ICE candidate "STR_FORMAT" with higher " "priority " STR_FORMAT":%lu", STR_FMT(&dup->foundation), STR_FMT(&cand->foundation), cand->component_id); } /* priority and foundation may change */ g_hash_table_remove(ag->foundation_hash, dup); recalc += __copy_cand(call, dup, cand); } else { ilog(LOG_DEBUG, "Learning new ICE candidate "STR_FORMAT":%lu", STR_FMT(&cand->foundation), cand->component_id); dup = g_slice_alloc(sizeof(*dup)); __copy_cand(call, dup, cand); g_hash_table_insert(ag->candidate_hash, dup, dup); g_queue_push_tail(&ag->remote_candidates, dup); } g_hash_table_insert(ag->foundation_hash, dup, dup); pair: if (!ps) continue; for (k = ag->local_interface->list.head; k; k = k->next) { /* skip duplicates here also */ if (__pair_lookup(ag, dup, k->data)) continue; __pair_candidate(k->data, ag, dup, ps); } } if (comps) ag->active_components = comps; if (!ag->active_components) { /* determine components for tricke-ice case */ comps = 2; if (!components[1]) comps = 1; ag->active_components = comps; } /* if we're here, we can start our ICE checks */ if (recalc) __recalc_pair_prios(ag); else __all_pairs_list(ag); if (comps) __do_ice_checks(ag); else __agent_shutdown(ag); }