Example #1
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;
}
Example #2
0
static int replace_network_address(struct sdp_chopper *chop, struct network_address *address,
		struct packet_stream *ps, struct sdp_ng_flags *flags)
{
	char buf[64];
	int len;
	struct packet_stream *sink = packet_stream_sink(ps);

	if (is_addr_unspecified(&address->parsed)
			&& !(sink && is_trickle_ice_address(&sink->advertised_endpoint)))
		return 0;

	if (copy_up_to(chop, &address->address_type))
		return -1;

	if (flags->media_address.s && is_addr_unspecified(&flags->parsed_media_address))
		__parse_address(&flags->parsed_media_address, NULL, NULL, &flags->media_address);

	if (!is_addr_unspecified(&flags->parsed_media_address))
		len = sprintf(buf, "%s %s",
				flags->parsed_media_address.family->rfc_name,
				sockaddr_print_buf(&flags->parsed_media_address));
	else
		call_stream_address46(buf, ps, SAF_NG, &len, NULL);
	chopper_append_dup(chop, buf, len);

	if (skip_over(chop, &address->address))
		return -1;

	return 0;
}
Example #3
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;
}
Example #4
0
static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp,
		struct sdp_ng_flags *flags, struct sdp_media *sdp_media)
{
	const struct local_intf *ifa;
	struct call_media *media;
	struct ice_agent *ag;
	unsigned int type_pref, local_pref;
	enum ice_candidate_type cand_type;
	struct ice_candidate *cand;

	media = rtp->media;

	cand_type = ICT_HOST;
	if (flags->ice_force_relay)
		cand_type = ICT_RELAY;
	if (MEDIA_ISSET(media, PASSTHRU))
		new_priority(sdp_media, cand_type, &type_pref, &local_pref);
	else {
		type_pref = ice_type_preference(cand_type);
		local_pref = -1;
	}

	ag = media->ice_agent;

	if (ag && AGENT_ISSET(ag, COMPLETED)) {
		ifa = rtp->selected_sfd->local_intf;
		insert_candidate(chop, rtp->selected_sfd, type_pref, ifa->unique_id, cand_type);
		if (rtcp) /* rtcp-mux only possible in answer */
			insert_candidate(chop, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type);

		if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) {
			GQueue rc;
			GList *l;
			chopper_append_c(chop, "a=remote-candidates:");
			ice_remote_candidates(&rc, ag);
			for (l = rc.head; l; l = l->next) {
				if (l != rc.head)
					chopper_append_c(chop, " ");
				cand = l->data;
				chopper_append_printf(chop, "%lu %s %u", cand->component_id,
						sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port);
			}
			chopper_append_c(chop, "\r\n");
			g_queue_clear(&rc);
		}
		return;
	}

	insert_sfd_candidates(chop, rtp, type_pref, local_pref, cand_type);

	if (rtcp) /* rtcp-mux only possible in answer */
		insert_sfd_candidates(chop, rtcp, type_pref, local_pref, cand_type);
}
Example #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));

}
Example #6
0
struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const sockaddr_t *addr) {
	struct callmaster *m = c->callmaster;
	struct control_ng_stats* cur;

	mutex_lock(&m->cngs_lock);
	cur = g_hash_table_lookup(m->cngs_hash, addr);
	if (!cur) {
		cur = g_slice_alloc0(sizeof(struct control_ng_stats));
		cur->proxy = *addr;
		ilog(LOG_DEBUG,"Adding a proxy for control ng stats:%s", sockaddr_print_buf(addr));
		g_hash_table_insert(m->cngs_hash, &cur->proxy, cur);
	}
	mutex_unlock(&m->cngs_lock);
	return cur;
}
Example #7
0
// st->stream->out_lock (or call->master_lock/W) must be held already
static int send_timer_send(struct send_timer *st, struct codec_packet *cp) {
	if (cp->to_send.tv_sec && timeval_cmp(&cp->to_send, &rtpe_now) > 0)
		return -1; // not yet

	if (!st->sink->selected_sfd)
		goto out;

	struct rtp_header *rh = (void *) cp->s.s;
	ilog(LOG_DEBUG, "Forward to sink endpoint: %s%s:%d%s (RTP seq %u TS %u)",
			FMT_M(sockaddr_print_buf(&st->sink->endpoint.address),
			st->sink->endpoint.port),
			ntohs(rh->seq_num),
			ntohl(rh->timestamp));

	socket_sendto(&st->sink->selected_sfd->socket,
			cp->s.s, cp->s.len, &st->sink->endpoint);

out:
	codec_packet_free(cp);

	return 0;
}
Example #8
0
/* call is locked in R */
int ice_response(struct stream_fd *sfd, const endpoint_t *src,
		struct stun_attrs *attrs, u_int32_t transaction[3])
{
	struct ice_candidate_pair *pair, *opair;
	struct ice_agent *ag;
	struct packet_stream *ps = sfd->stream;
	struct call_media *media = ps->media;
	const char *err;
	unsigned int component;
	struct ice_candidate *cand;
	const struct local_intf *ifa;
	int ret, was_ctl;

	__DBG("received ICE response from %s on %s", endpoint_print_buf(src),
			endpoint_print_buf(&sfd->socket.local));

	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_intf;

	ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s",
			attrs->error_code, PAIR_FMT(pair),
			endpoint_print_buf(&pair->remote_candidate->endpoint),
			sockaddr_print_buf(&ifa->spec->local_address.addr));

	/* verify endpoints */
	err = "ICE/STUN response received, but source address didn't match remote candidate address";
	if (!endpoint_eq(src, &pair->remote_candidate->endpoint))
		goto err;

	err = "ICE/STUN response received, but destination address didn't match local interface address";
	if (pair->sfd != sfd)
		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, endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local));

	if (pair && attrs->error_code)
		__fail_pair(pair);

	return 0;
}
Example #9
0
static int if_addr_parse(GQueue *q, char *s, struct ifaddrs *ifas) {
	str name;
	char *c;
	sockaddr_t *addr, adv;
	GQueue addrs = G_QUEUE_INIT;
	struct intf_config *ifa;

	/* name */
	c = strchr(s, '/');
	if (c) {
		*c++ = 0;
		str_init(&name, s);
		s = c;
	}
	else
		str_init(&name, "default");

	/* advertised address */
	c = strchr(s, '!');
	if (c)
		*c++ = 0;

	/* address */
	addr = g_slice_alloc(sizeof(*addr));
	if (!sockaddr_parse_any(addr, s)) {
		if (is_addr_unspecified(addr))
			return -1;
		g_queue_push_tail(&addrs, addr);
	}
	else {
		// could be an interface name?
		ilog(LOG_DEBUG, "Could not parse '%s' as network address, checking to see if "
				"it's an interface", s);
		for (struct ifaddrs *ifa = ifas; ifa; ifa = ifa->ifa_next) {
			if (strcmp(ifa->ifa_name, s))
				continue;
			if (!(ifa->ifa_flags & IFF_UP))
				continue;
			if (!ifa->ifa_addr)
				continue;
			if (ifa->ifa_addr->sa_family == AF_INET) {
				struct sockaddr_in *sin = (void *) ifa->ifa_addr;
				addr->family = __get_socket_family_enum(SF_IP4);
				addr->u.ipv4 = sin->sin_addr;
			}
			else if (ifa->ifa_addr->sa_family == AF_INET6) {
				struct sockaddr_in6 *sin = (void *) ifa->ifa_addr;
				if (sin->sin6_scope_id)
					continue; // link-local
				addr->family = __get_socket_family_enum(SF_IP6);
				addr->u.ipv6 = sin->sin6_addr;
			}
			else
				continue;

			// got one
			ilog(LOG_DEBUG, "Determined address %s for interface '%s'",
					sockaddr_print_buf(addr), s);
			g_queue_push_tail(&addrs, addr);
			addr = g_slice_alloc(sizeof(*addr));
		}

		// free last unused entry
		g_slice_free1(sizeof(*addr), addr);
	}

	if (!addrs.length) // nothing found
		return -1;

	ZERO(adv);
	if (c) {
		if (sockaddr_parse_any(&adv, c))
			return -1;
		if (is_addr_unspecified(&adv))
			return -1;
	}

	while ((addr = g_queue_pop_head(&addrs))) {
		ifa = g_slice_alloc0(sizeof(*ifa));
		ifa->name = name;
		ifa->local_address.addr = *addr;
		ifa->local_address.type = socktype_udp;
		ifa->advertised_address.addr = adv;
		if (is_addr_unspecified(&ifa->advertised_address.addr))
			ifa->advertised_address.addr = *addr;
		ifa->advertised_address.type = ifa->local_address.type;
		ifa->port_min = rtpe_config.port_min;
		ifa->port_max = rtpe_config.port_max;

		// handle "base:suffix" separation for round-robin selection
		ifa->name_rr_spec = ifa->name;
		str_token(&ifa->name_base, &ifa->name_rr_spec, ':'); // sets name_rr_spec to null string if no ':' found

		g_queue_push_tail(q, ifa);

		g_slice_free1(sizeof(*addr), addr);
	}

	return 0;
}