summaryrefslogtreecommitdiff
path: root/pjnath/src/pjnath/ice_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjnath/src/pjnath/ice_session.c')
-rw-r--r--pjnath/src/pjnath/ice_session.c176
1 files changed, 115 insertions, 61 deletions
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
index 8b0538d8..ccf545c5 100644
--- a/pjnath/src/pjnath/ice_session.c
+++ b/pjnath/src/pjnath/ice_session.c
@@ -21,6 +21,7 @@
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/guid.h>
+#include <pj/hash.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
@@ -101,6 +102,11 @@ typedef struct timer_data
} timer_data;
+/* This is the data that will be attached as token to outgoing
+ * STUN messages.
+ */
+
+
/* Forward declarations */
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason);
@@ -169,6 +175,21 @@ PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type)
}
+PJ_DEF(const char*) pj_ice_sess_role_name(pj_ice_sess_role role)
+{
+ switch (role) {
+ case PJ_ICE_SESS_ROLE_UNKNOWN:
+ return "Unknown";
+ case PJ_ICE_SESS_ROLE_CONTROLLED:
+ return "Controlled";
+ case PJ_ICE_SESS_ROLE_CONTROLLING:
+ return "Controlling";
+ default:
+ return "??";
+ }
+}
+
+
/* Get the prefix for the foundation */
static int get_type_prefix(pj_ice_cand_type type)
{
@@ -183,17 +204,28 @@ static int get_type_prefix(pj_ice_cand_type type)
}
}
-/* Calculate foundation */
+/* Calculate foundation:
+ * Two candidates have the same foundation when they are "similar" - of
+ * the same type and obtained from the same host candidate and STUN
+ * server using the same protocol. Otherwise, their foundation is
+ * different.
+ */
PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
pj_str_t *foundation,
pj_ice_cand_type type,
const pj_sockaddr *base_addr)
{
char buf[64];
+ pj_uint32_t val;
+ if (base_addr->addr.sa_family == pj_AF_INET()) {
+ val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr);
+ } else {
+ val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr),
+ pj_sockaddr_get_addr_len(base_addr));
+ }
pj_ansi_snprintf(buf, sizeof(buf), "%c%x",
- get_type_prefix(type),
- (int)pj_ntohl(base_addr->ipv4.sin_addr.s_addr));
+ get_type_prefix(type), val);
pj_strdup2(pool, foundation, buf);
}
@@ -263,7 +295,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL);
if (name == NULL)
- name = "ice%p";
+ name = "icess%p";
pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS,
PJNATH_POOL_INC_ICE_SESS, NULL);
@@ -300,6 +332,12 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
}
}
+ /* Initialize transport datas */
+ for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+ ice->tp_data[i].transport_id = i;
+ ice->tp_data[i].has_req_data = PJ_FALSE;
+ }
+
if (local_ufrag == NULL) {
ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN);
pj_create_random_string(ice->rx_ufrag.ptr, PJ_ICE_UFRAG_LEN);
@@ -551,6 +589,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
*/
PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
unsigned comp_id,
+ unsigned transport_id,
pj_ice_cand_type type,
pj_uint16_t local_pref,
const pj_str_t *foundation,
@@ -576,17 +615,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
}
lcand = &ice->lcand[ice->lcand_cnt];
- lcand->comp_id = comp_id;
+ lcand->comp_id = (pj_uint8_t)comp_id;
+ lcand->transport_id = (pj_uint8_t)transport_id;
lcand->type = type;
pj_strdup(ice->pool, &lcand->foundation, foundation);
lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
pj_memcpy(&lcand->addr, addr, addr_len);
pj_memcpy(&lcand->base_addr, base_addr, addr_len);
- if (rel_addr)
- pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
- else
- pj_bzero(&lcand->rel_addr, sizeof(lcand->rel_addr));
-
+ pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
LOG4((ice->obj_name,
@@ -1322,9 +1358,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
}
/* Disable our components which don't have matching component */
- if (ice->comp_cnt==2 && highest_comp==1) {
- ice->comp_cnt = 1;
+ for (i=highest_comp; i<ice->comp_cnt; ++i) {
+ if (ice->comp[i].stun_sess) {
+ pj_stun_session_destroy(ice->comp[i].stun_sess);
+ pj_bzero(&ice->comp[i], sizeof(ice->comp[i]));
+ }
}
+ ice->comp_cnt = highest_comp;
/* Init timer entry in the checklist. Initially the timer ID is FALSE
* because timer is not running.
@@ -1345,26 +1385,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
return PJ_SUCCESS;
}
-
-/* This is the data that will be attached as user data to outgoing
- * STUN requests, and it will be given back when we receive completion
- * status of the request.
- */
-struct req_data
-{
- pj_ice_sess *ice;
- pj_ice_sess_checklist *clist;
- unsigned ckid;
-};
-
-
/* Perform check on the specified candidate pair */
static pj_status_t perform_check(pj_ice_sess *ice,
pj_ice_sess_checklist *clist,
unsigned check_id)
{
pj_ice_sess_comp *comp;
- struct req_data *rd;
+ pj_ice_msg_data *msg_data;
pj_ice_sess_check *check;
const pj_ice_sess_cand *lcand;
const pj_ice_sess_cand *rcand;
@@ -1392,10 +1419,12 @@ static pj_status_t perform_check(pj_ice_sess *ice,
/* Attach data to be retrieved later when STUN request transaction
* completes and on_stun_request_complete() callback is called.
*/
- rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
- rd->ice = ice;
- rd->clist = clist;
- rd->ckid = check_id;
+ msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data);
+ msg_data->transport_id = lcand->transport_id;
+ msg_data->has_req_data = PJ_TRUE;
+ msg_data->data.req.ice = ice;
+ msg_data->data.req.clist = clist;
+ msg_data->data.req.ckid = check_id;
/* Add PRIORITY */
prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535,
@@ -1427,7 +1456,7 @@ static pj_status_t perform_check(pj_ice_sess *ice,
*/
/* Initiate STUN transaction to send the request */
- status = pj_stun_session_send_msg(comp->stun_sess, (void*)rd, PJ_FALSE,
+ status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE,
PJ_TRUE, &rcand->addr,
sizeof(pj_sockaddr_in), check->tdata);
if (status != PJ_SUCCESS) {
@@ -1655,12 +1684,10 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess,
{
stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
pj_ice_sess *ice = sd->ice;
-
- PJ_UNUSED_ARG(token);
-
- return (*ice->cb.on_tx_pkt)(ice, sd->comp_id,
- pkt, pkt_size,
- dst_addr, addr_len);
+ pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
+
+ return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
+ pkt, pkt_size, dst_addr, addr_len);
}
@@ -1673,7 +1700,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
- struct req_data *rd = (struct req_data*) token;
+ pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
pj_ice_sess *ice;
pj_ice_sess_check *check, *new_check;
pj_ice_sess_cand *lcand;
@@ -1684,9 +1711,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
PJ_UNUSED_ARG(stun_sess);
PJ_UNUSED_ARG(src_addr_len);
- ice = rd->ice;
- check = &rd->clist->checks[rd->ckid];
- clist = rd->clist;
+ pj_assert(msg_data->has_req_data);
+
+ ice = msg_data->data.req.ice;
+ clist = msg_data->data.req.clist;
+ check = &clist->checks[msg_data->data.req.ckid];
+
/* Mark STUN transaction as complete */
pj_assert(tdata == check->tdata);
@@ -1739,7 +1769,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
/* Resend request */
LOG4((ice->obj_name, "Resending check because of role conflict"));
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
- perform_check(ice, clist, rd->ckid);
+ perform_check(ice, clist, msg_data->data.req.ckid);
pj_mutex_unlock(ice->mutex);
return;
}
@@ -1846,6 +1876,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
/* Add new peer reflexive candidate */
status = pj_ice_sess_add_cand(ice, check->lcand->comp_id,
+ msg_data->transport_id,
PJ_ICE_CAND_TYPE_PRFLX,
65535, &foundation,
&xaddr->sockaddr,
@@ -1919,6 +1950,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
{
stun_data *sd;
const pj_stun_msg *msg = rdata->msg;
+ pj_ice_msg_data *msg_data;
pj_ice_sess *ice;
pj_stun_priority_attr *prio_attr;
pj_stun_use_candidate_attr *uc_attr;
@@ -1929,12 +1961,11 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
- PJ_UNUSED_ARG(token);
-
+
/* Reject any requests except Binding request */
if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
- NULL, NULL, PJ_TRUE,
+ NULL, token, PJ_TRUE,
src_addr, src_addr_len);
return PJ_SUCCESS;
}
@@ -2001,7 +2032,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
} else {
/* Generate 487 response */
pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT,
- NULL, NULL, PJ_TRUE,
+ NULL, token, PJ_TRUE,
src_addr, src_addr_len);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
@@ -2013,7 +2044,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) {
/* Generate 487 response */
pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT,
- NULL, NULL, PJ_TRUE,
+ NULL, token, PJ_TRUE,
src_addr, src_addr_len);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
@@ -2034,11 +2065,18 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
return status;
}
+ /* Add XOR-MAPPED-ADDRESS attribute */
status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
PJ_STUN_ATTR_XOR_MAPPED_ADDR,
PJ_TRUE, src_addr, src_addr_len);
- status = pj_stun_session_send_msg(sess, NULL, PJ_TRUE, PJ_TRUE,
+ /* Create a msg_data to be associated with this response */
+ msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
+ msg_data->transport_id = ((pj_ice_msg_data*)token)->transport_id;
+ msg_data->has_req_data = PJ_FALSE;
+
+ /* Send the response */
+ status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE,
src_addr, src_addr_len, tdata);
@@ -2058,6 +2096,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
/* Init rcheck */
rcheck->comp_id = sd->comp_id;
+ rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
rcheck->src_addr_len = src_addr_len;
pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len);
rcheck->use_candidate = (uc_attr != NULL);
@@ -2090,7 +2129,6 @@ static void handle_incoming_check(pj_ice_sess *ice,
pj_ice_sess_cand *lcand = NULL;
pj_ice_sess_cand *rcand;
unsigned i;
- pj_bool_t is_relayed;
comp = find_comp(ice, rcheck->comp_id);
@@ -2109,7 +2147,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
*/
if (i == ice->rcand_cnt) {
rcand = &ice->rcand[ice->rcand_cnt++];
- rcand->comp_id = rcheck->comp_id;
+ rcand->comp_id = (pj_uint8_t)rcheck->comp_id;
rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
rcand->prio = rcheck->priority;
pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len);
@@ -2147,12 +2185,14 @@ static void handle_incoming_check(pj_ice_sess *ice,
}
}
#else
- /* Just get candidate with the highest priority for the specified
- * component ID in the checklist.
+ /* Just get candidate with the highest priority and same transport ID
+ * for the specified component ID in the checklist.
*/
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
- if (c->lcand->comp_id == rcheck->comp_id) {
+ if (c->lcand->comp_id == rcheck->comp_id &&
+ c->lcand->transport_id == rcheck->transport_id)
+ {
lcand = c->lcand;
break;
}
@@ -2170,11 +2210,6 @@ static void handle_incoming_check(pj_ice_sess *ice,
/*
* Create candidate pair for this request.
*/
- /* First check if the source address is the source address of the
- * STUN relay, to determine if local candidate is relayed candidate.
- */
- PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE);
- is_relayed = PJ_FALSE;
/*
* 7.2.1.4. Triggered Checks
@@ -2309,6 +2344,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
{
pj_status_t status = PJ_SUCCESS;
pj_ice_sess_comp *comp;
+ pj_ice_sess_cand *cand;
PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL);
@@ -2332,7 +2368,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
goto on_return;
}
- status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len,
+ cand = comp->valid_check->lcand;
+ status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand->transport_id,
+ data, data_len,
&comp->valid_check->rcand->addr,
sizeof(pj_sockaddr_in));
@@ -2344,6 +2382,7 @@ on_return:
PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
unsigned comp_id,
+ unsigned transport_id,
void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *src_addr,
@@ -2351,6 +2390,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
{
pj_status_t status = PJ_SUCCESS;
pj_ice_sess_comp *comp;
+ pj_ice_msg_data *msg_data = NULL;
+ unsigned i;
pj_status_t stun_status;
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
@@ -2363,11 +2404,24 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
goto on_return;
}
+ /* Find transport */
+ for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+ if (ice->tp_data[i].transport_id == transport_id) {
+ msg_data = &ice->tp_data[i];
+ break;
+ }
+ }
+ if (msg_data == NULL) {
+ pj_assert(!"Invalid transport ID");
+ status = PJ_EINVAL;
+ goto on_return;
+ }
+
stun_status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size,
PJ_STUN_IS_DATAGRAM);
if (stun_status == PJ_SUCCESS) {
status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
- PJ_STUN_IS_DATAGRAM, NULL,
+ PJ_STUN_IS_DATAGRAM, msg_data,
NULL, src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg));
@@ -2375,7 +2429,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
ice->tmp.errmsg));
}
} else {
- (*ice->cb.on_rx_data)(ice, comp_id, pkt, pkt_size,
+ (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size,
src_addr, src_addr_len);
}