Beispiel #1
0
/* agent must be locked */
static struct ice_candidate_pair *__pair_candidate(struct interface_address *addr, struct ice_agent *ag,
		struct ice_candidate *cand, struct packet_stream *ps)
{
	struct ice_candidate_pair *pair;

	if (addr->family != family_from_address(&cand->endpoint.ip46))
		return NULL;

	pair = g_slice_alloc0(sizeof(*pair));

	pair->agent = ag;
	pair->remote_candidate = cand;
	pair->local_address = addr;
	pair->packet_stream = ps;
	if (cand->component_id != 1)
		PAIR_SET(pair, FROZEN);
	__do_ice_pair_priority(pair);
	__new_stun_transaction(pair);

	g_queue_push_tail(&ag->candidate_pairs, pair);
	g_hash_table_insert(ag->pair_hash, pair, pair);
	g_tree_insert(ag->all_pairs, pair, pair);

	ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair),
			smart_ntop_buf(&addr->addr), smart_ntop_ep_buf(&cand->endpoint),
			ice_candidate_type_str(cand->type));

	return pair;
}
Beispiel #2
0
/* agent must be locked */
static struct ice_candidate_pair *__pair_candidate(struct stream_fd *sfd, struct ice_agent *ag,
		struct ice_candidate *cand)
{
	struct ice_candidate_pair *pair;

	if (sfd->socket.family != cand->endpoint.address.family)
		return NULL;

	pair = g_slice_alloc0(sizeof(*pair));

	pair->agent = ag;
	pair->remote_candidate = cand;
	pair->local_intf = sfd->local_intf;
	pair->sfd = sfd;
	if (cand->component_id != 1)
		PAIR_SET(pair, FROZEN);
	__do_ice_pair_priority(pair);
	__new_stun_transaction(pair);

	g_queue_push_tail(&ag->candidate_pairs, pair);
	g_hash_table_insert(ag->pair_hash, pair, pair);
	g_tree_insert(ag->all_pairs, pair, pair);

	ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair),
			sockaddr_print_buf(&sfd->socket.local.address),
			endpoint_print_buf(&cand->endpoint),
			ice_candidate_type_str(cand->type));

	return pair;
}
Beispiel #3
0
/* agent must NOT be locked, but call must be locked in R */
static void __do_ice_check(struct ice_candidate_pair *pair) {
	struct sockaddr_in6 dst;
	struct packet_stream *ps = pair->packet_stream;
	struct ice_agent *ag = pair->agent;
	u_int32_t prio, transact[3];

	if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE))
		return;

	if (!ag->pwd[0].s)
		return;

	ZERO(dst);
	dst.sin6_port = htons(pair->remote_candidate->endpoint.port);
	dst.sin6_addr = pair->remote_candidate->endpoint.ip46;
	dst.sin6_family = AF_INET6;

	prio = ice_priority(ICT_PRFLX, pair->local_address->preference,
			pair->remote_candidate->component_id);

	mutex_lock(&ag->lock);

	pair->retransmit = g_now;
	if (!PAIR_SET(pair, IN_PROGRESS)) {
		PAIR_CLEAR2(pair, FROZEN, FAILED);
		pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL;
		pair->retransmits = 0;
	}
	else if (pair->retransmits > STUN_MAX_RETRANSMITS) {
		__fail_pair(pair);
		mutex_unlock(&ag->lock);
		return;
	}
	else {
		pair->retransmit_ms *= 2;
		pair->retransmits++;
	}
	timeval_add_usec(&pair->retransmit, pair->retransmit_ms * 1000);
	__agent_schedule_abs(pair->agent, &pair->retransmit);
	memcpy(transact, pair->stun_transaction, sizeof(transact));

	pair->was_controlling = AGENT_ISSET(ag, CONTROLLING);
	pair->was_nominated = PAIR_ISSET(pair, TO_USE);

	mutex_unlock(&ag->lock);

	ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s",
			PAIR_ISSET(pair, TO_USE) ? "nominating " : "",
			PAIR_FMT(pair), smart_ntop_buf(&pair->local_address->addr),
			smart_ntop_ep_buf(&pair->remote_candidate->endpoint));

	stun_binding_request(&dst, transact, &ag->pwd[0], ag->ufrag,
			AGENT_ISSET(ag, CONTROLLING), tie_breaker,
			prio, &pair->local_address->addr, ps->sfd->fd.fd,
			PAIR_ISSET(pair, TO_USE));

}
Beispiel #4
0
/* call(W) or call(R)+agent must be locked - no in_lock or out_lock must be held */
static int __check_valid(struct ice_agent *ag) {
	struct call_media *media = ag->media;
	struct packet_stream *ps;
	GList *l, *k, *m;
	GQueue all_compos;
	struct ice_candidate_pair *pair;
//	const struct local_intf *ifa;
	struct stream_fd *sfd;

	if (!ag) {
		ilog(LOG_ERR, "ice ag is NULL");
		return 0;
	}

	__get_complete_valid_pairs(&all_compos, ag);

	if (!all_compos.length) {
		ilog(LOG_DEBUG, "ICE not completed yet");
		return 0;
	}

	pair = all_compos.head->data;
	ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair));
	AGENT_SET(ag, COMPLETED);

	for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) {
		ps = l->data;
		pair = k->data;

		mutex_lock(&ps->out_lock);
		if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) {
			ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component,
					endpoint_print_buf(&pair->remote_candidate->endpoint));
			ps->endpoint = pair->remote_candidate->endpoint;
		}
		mutex_unlock(&ps->out_lock);

		for (m = ps->sfds.head; m; m = m->next) {
			sfd = m->data;
			if (sfd->local_intf != pair->local_intf)
				continue;
			ps->selected_sfd = sfd;
			if (ps->component == 1)
				ilog(LOG_INFO, "ICE negotiated: local interface %s",
						sockaddr_print_buf(&pair->local_intf->spec->local_address.addr));
		}
	}

	call_media_unkernelize(media);

	g_queue_clear(&all_compos);
	return 1;
}
Beispiel #5
0
/* agent must NOT be locked, but call must be locked in R */
static void __do_ice_check(struct ice_candidate_pair *pair) {
	struct stream_fd *sfd = pair->sfd;
	struct ice_agent *ag = pair->agent;
	u_int32_t prio, transact[3];

	if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE))
		return;

	if (!ag->pwd[0].s)
		return;

	prio = ice_priority(ICT_PRFLX, pair->local_intf->unique_id,
			pair->remote_candidate->component_id);

	mutex_lock(&ag->lock);

	pair->retransmit = g_now;
	if (!PAIR_SET(pair, IN_PROGRESS)) {
		PAIR_CLEAR2(pair, FROZEN, FAILED);
		pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL;
		pair->retransmits = 0;
	}
	else if (pair->retransmits > STUN_MAX_RETRANSMITS) {
		__fail_pair(pair);
		mutex_unlock(&ag->lock);
		return;
	}
	else {
		pair->retransmit_ms *= 2;
		pair->retransmits++;
	}
	timeval_add_usec(&pair->retransmit, pair->retransmit_ms * 1000);
	__agent_schedule_abs(pair->agent, &pair->retransmit);
	memcpy(transact, pair->stun_transaction, sizeof(transact));

	pair->was_controlling = AGENT_ISSET(ag, CONTROLLING);
	pair->was_nominated = PAIR_ISSET(pair, TO_USE);

	mutex_unlock(&ag->lock);

	ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s",
			PAIR_ISSET(pair, TO_USE) ? "nominating " : "",
			PAIR_FMT(pair), sockaddr_print_buf(&pair->local_intf->spec->local_address.addr),
			endpoint_print_buf(&pair->remote_candidate->endpoint));

	stun_binding_request(&pair->remote_candidate->endpoint, transact, &ag->pwd[0], ag->ufrag,
			AGENT_ISSET(ag, CONTROLLING), tie_breaker,
			prio, &sfd->socket,
			PAIR_ISSET(pair, TO_USE));

}
Beispiel #6
0
/* agent must NOT be locked */
static void __trigger_check(struct ice_candidate_pair *pair) {
	struct ice_agent *ag = pair->agent;

	ilog(LOG_DEBUG, "Triggering check for "PAIR_FORMAT, PAIR_FMT(pair));

	mutex_lock(&ag->lock);
	pair->retransmits = 0;
	if (PAIR_CLEAR(pair, FAILED))
		PAIR_CLEAR(pair, IN_PROGRESS);
	if (ag->triggered.length < 4 * MAX_ICE_CANDIDATES && !PAIR_SET(pair, TRIGGERED))
		g_queue_push_tail(&ag->triggered, pair);
	mutex_unlock(&ag->lock);

	__agent_schedule(ag, 0);
}
Beispiel #7
0
/* call(W) or call(R)+agent must be locked - no in_lock or out_lock must be held */
static int __check_valid(struct ice_agent *ag) {
	struct call_media *media = ag->media;
	struct packet_stream *ps;
	GList *l, *k;
	GQueue all_compos;
	struct ice_candidate_pair *pair;
	struct interface_address *ifa;

	if (!ag) {
		ilog(LOG_ERR, "ice ag is NULL");
		return 0;
	}

	__get_complete_valid_pairs(&all_compos, ag);

	if (!all_compos.length) {
		ilog(LOG_DEBUG, "ICE not completed yet");
		return 0;
	}

	pair = all_compos.head->data;
	ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair));
	AGENT_SET(ag, COMPLETED);

	ifa = g_atomic_pointer_get(&media->local_address);
	if (ifa != pair->local_address
			&& g_atomic_pointer_compare_and_exchange(&media->local_address, ifa,
				pair->local_address))
		ilog(LOG_INFO, "ICE negotiated: local interface %s", smart_ntop_buf(&pair->local_address->addr));

	for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) {
		ps = l->data;
		pair = k->data;

		mutex_lock(&ps->out_lock);
		if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) {
			ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component,
					smart_ntop_ep_buf(&pair->remote_candidate->endpoint));
			ps->endpoint = pair->remote_candidate->endpoint;
		}
		mutex_unlock(&ps->out_lock);
	}

	call_media_unkernelize(media);

	g_queue_clear(&all_compos);
	return 1;
}
Beispiel #8
0
static int __check_succeeded_complete(struct ice_agent *ag) {
	GQueue complete;
	int ret;

	__get_complete_succeeded_pairs(&complete, ag);
	if (complete.length) {
		struct ice_candidate_pair *pair = complete.head->data;
		ilog(LOG_DEBUG, "Best succeeded ICE pair with all components is "PAIR_FORMAT, PAIR_FMT(pair));
		ret = 1;
	}
	else {
		ilog(LOG_DEBUG, "No succeeded ICE pairs with all components yet");
		ret = 0;
	}
	g_queue_clear(&complete);
	return ret;
}
Beispiel #9
0
static void __nominate_pairs(struct ice_agent *ag) {
	GQueue complete;
	GList *l;
	struct ice_candidate_pair *pair;

	ilog(LOG_DEBUG, "Start nominating ICE pairs");

	AGENT_SET(ag, NOMINATING);
	ZERO(ag->start_nominating);

	__get_complete_succeeded_pairs(&complete, ag);

	for (l = complete.head; l; l = l->next) {
		pair = l->data;
		ilog(LOG_DEBUG, "Nominating ICE pair "PAIR_FORMAT, PAIR_FMT(pair));
		PAIR_CLEAR(pair, IN_PROGRESS);
		PAIR_SET2(pair, NOMINATED, TO_USE);
		pair->retransmits = 0;
		__new_stun_transaction(pair);
		g_queue_push_tail(&ag->triggered, pair);
	}

	g_queue_clear(&complete);
}
Beispiel #10
0
static void __fail_pair(struct ice_candidate_pair *pair) {
	ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as failed", PAIR_FMT(pair));
	PAIR_SET(pair, FAILED);
	PAIR_CLEAR(pair, IN_PROGRESS);
}
Beispiel #11
0
/* 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;
}
Beispiel #12
0
/* return values:
 * 1 = ICE completed, interfaces selected
 * 0 = packet processed
 * -1 = generic error, process packet as normal
 * -2 = role conflict
 */
int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst,
		struct stun_attrs *attrs)
{
	struct call_media *media = ps->media;
	struct ice_agent *ag;
	struct interface_address *ifa;
	const char *err;
	struct ice_candidate *cand;
	struct ice_candidate_pair *pair;
	int ret;

	__DBG("received ICE request 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);

	ifa = get_interface_from_address(ag->local_interface, dst);
	err = "ICE/STUN binding request received on unknown local interface address";
	if (!ifa)
		goto err;

	/* determine candidate pair */
	mutex_lock(&ag->lock);

	cand = __cand_lookup(ag, src, ps->component);

	if (!cand)
		pair = __learned_candidate(ag, ps, src, ifa, attrs->priority);
	else
		pair = __pair_lookup(ag, cand, ifa);

	err = "Failed to determine ICE candidate from STUN request";
	if (!pair)
		goto err_unlock;

	mutex_unlock(&ag->lock);

	/* determine role conflict */
	if (attrs->controlling && AGENT_ISSET(ag, CONTROLLING)) {
		if (tie_breaker >= attrs->tiebreaker)
			return -2;
		else
			__role_change(ag, 0);
	}
	else if (attrs->controlled && !AGENT_ISSET(ag, CONTROLLING)) {
		if (tie_breaker >= attrs->tiebreaker)
			__role_change(ag, 1);
		else
			return -2;
	}


	if (PAIR_ISSET(pair, SUCCEEDED))
		;
	else
		__trigger_check(pair);

	ret = 0;

	if (attrs->use && !PAIR_SET(pair, NOMINATED)) {
		ilog(LOG_DEBUG, "ICE pair "PAIR_FORMAT" has been nominated by peer", PAIR_FMT(pair));

		mutex_lock(&ag->lock);

		g_tree_insert(ag->nominated_pairs, pair, pair);

		if (PAIR_ISSET(pair, SUCCEEDED)) {
			PAIR_SET(pair, VALID);
			g_tree_insert(ag->valid_pairs, pair, pair);
		}

		if (!AGENT_ISSET(ag, CONTROLLING))
			ret = __check_valid(ag);

		mutex_unlock(&ag->lock);
	}

	return ret;

err_unlock:
	mutex_unlock(&ag->lock);
err:
	ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, smart_ntop_port_buf(src), smart_ntop_buf(dst));
	return 0;
}