/* 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; }
/* initializes "out" */ static void __get_complete_components(GQueue *out, struct ice_agent *ag, GTree *t, unsigned int flag) { GQueue compo1 = G_QUEUE_INIT; GList *l; struct ice_candidate_pair *pair1, *pairX; struct ice_candidate *cand; unsigned int i; __get_pairs_by_component(&compo1, t, 1); g_queue_init(out); for (l = compo1.head; l; l = l->next) { pair1 = l->data; g_queue_clear(out); g_queue_push_tail(out, pair1); for (i = 2; i <= ag->active_components; i++) { cand = __foundation_lookup(ag, &pair1->remote_candidate->foundation, i); if (!cand) goto next_foundation; pairX = __pair_lookup(ag, cand, pair1->local_address); if (!pairX) goto next_foundation; if (!bf_isset(&pairX->pair_flags, flag)) goto next_foundation; g_queue_push_tail(out, pairX); } goto found; next_foundation: ; } /* nothing found */ g_queue_clear(out); found: g_queue_clear(&compo1); }
/* call is locked in R */ int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, struct stun_attrs *attrs, u_int32_t transaction[3]) { struct ice_candidate_pair *pair, *opair; struct ice_agent *ag; struct call_media *media = ps->media; const char *err; unsigned int component; struct ice_candidate *cand; struct interface_address *ifa; int ret, was_ctl; __DBG("received ICE response from %s on %s", smart_ntop_port_buf(src), smart_ntop_buf(dst)); ag = media->ice_agent; if (!ag) return -1; atomic64_set(&ag->last_activity, poller_now); mutex_lock(&ag->lock); pair = g_hash_table_lookup(ag->transaction_hash, transaction); err = "ICE/STUN response with unknown transaction received"; if (!pair) goto err_unlock; was_ctl = pair->was_controlling; mutex_unlock(&ag->lock); ifa = pair->local_address; ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s", attrs->error_code, PAIR_FMT(pair), smart_ntop_ep_buf(&pair->remote_candidate->endpoint), smart_ntop_buf(&ifa->addr)); /* verify endpoints */ err = "ICE/STUN response received, but source address didn't match remote candidate address"; if (memcmp(&src->sin6_addr, &pair->remote_candidate->endpoint.ip46, sizeof(src->sin6_addr))) goto err; if (ntohs(src->sin6_port) != pair->remote_candidate->endpoint.port) goto err; err = "ICE/STUN response received, but destination address didn't match local interface address"; if (memcmp(dst, &ifa->addr, sizeof(*dst))) goto err; if (pair->packet_stream != ps) goto err; PAIR_CLEAR(pair, IN_PROGRESS); ret = 0; /* handle all errors */ if (attrs->error_code) { err = "ICE/STUN error received"; if (attrs->error_code != 487) goto err; __role_change(ag, !was_ctl); __trigger_check(pair); goto out; } /* we don't discover peer reflexive here (RFC 5245 7.1.3.2.1) as we don't expect to be behind NAT */ /* we also skip parts of 7.1.3.2.2 as we don't do server reflexive */ mutex_lock(&ag->lock); /* check if we're in the final (controlling) phase */ if (pair->was_nominated && PAIR_CLEAR(pair, TO_USE)) { ilog(LOG_DEBUG, "Setting nominated ICE candidate pair "PAIR_FORMAT" as valid", PAIR_FMT(pair)); PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); ret = __check_valid(ag); goto out_unlock; } if (PAIR_SET(pair, SUCCEEDED)) goto out_unlock; ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as succeeded", PAIR_FMT(pair)); g_tree_insert(ag->succeeded_pairs, pair, pair); if (!ag->start_nominating.tv_sec) { if (__check_succeeded_complete(ag)) { ag->start_nominating = g_now; timeval_add_usec(&ag->start_nominating, 100000); __agent_schedule_abs(ag, &ag->start_nominating); } } /* now unfreeze all other pairs from the same foundation */ for (component = 1; component <= MAX_COMPONENTS; component++) { if (component == ps->component) continue; cand = __foundation_lookup(ag, &pair->remote_candidate->foundation, component); if (!cand) continue; opair = __pair_lookup(ag, cand, ifa); if (!opair) continue; if (PAIR_ISSET(opair, FAILED)) continue; if (!PAIR_CLEAR(opair, FROZEN)) continue; ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(opair)); } /* if this was previously nominated by the peer, it's now valid */ if (PAIR_ISSET(pair, NOMINATED)) { PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); if (!AGENT_ISSET(ag, CONTROLLING)) ret = __check_valid(ag); } out_unlock: mutex_unlock(&ag->lock); out: return ret; err_unlock: mutex_unlock(&ag->lock); err: if (err) ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, smart_ntop_port_buf(src), smart_ntop_buf(dst)); if (pair && attrs->error_code) __fail_pair(pair); return 0; }