Esempio n. 1
0
/* 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);
}
Esempio n. 2
0
/* 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;
}
Esempio n. 3
0
/* 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;
}
Esempio n. 4
0
/* 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);
}