Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/ice/chklist.c b/src/ice/chklist.c
new file mode 100644
index 0000000..24e4f34
--- /dev/null
+++ b/src/ice/chklist.c
@@ -0,0 +1,341 @@
+/**
+ * @file chklist.c ICE Checklist
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include "ice.h"
+
+
+#define DEBUG_MODULE "chklist"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Forming Candidate Pairs
+ */
+static int candpairs_form(struct icem *icem)
+{
+ struct le *le;
+ int err = 0;
+
+ if (list_isempty(&icem->lcandl))
+ return ENOENT;
+
+ if (list_isempty(&icem->rcandl)) {
+ DEBUG_WARNING("%s: no remote candidates\n", icem->name);
+ return ENOENT;
+ }
+
+ for (le = icem->lcandl.head; le; le = le->next) {
+
+ struct ice_cand *lcand = le->data;
+ struct le *rle;
+
+ for (rle = icem->rcandl.head; rle; rle = rle->next) {
+
+ struct ice_cand *rcand = rle->data;
+
+ if (lcand->compid != rcand->compid)
+ continue;
+
+ if (sa_af(&lcand->addr) != sa_af(&rcand->addr))
+ continue;
+
+ err = icem_candpair_alloc(NULL, icem, lcand, rcand);
+ if (err)
+ return err;
+ }
+ }
+
+ return err;
+}
+
+
+/* Replace server reflexive candidates by its base */
+static const struct sa *cand_srflx_addr(const struct ice_cand *c)
+{
+ return (ICE_CAND_TYPE_SRFLX == c->type) ? &c->base->addr : &c->addr;
+}
+
+
+/* return: NULL to keep, pointer to remove object */
+static void *unique_handler(struct le *le1, struct le *le2)
+{
+ struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;
+
+ if (cp1->comp->id != cp2->comp->id)
+ return NULL;
+
+ if (!sa_cmp(cand_srflx_addr(cp1->lcand),
+ cand_srflx_addr(cp2->lcand), SA_ALL) ||
+ !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL))
+ return NULL;
+
+ return cp1->pprio < cp2->pprio ? cp1 : cp2;
+}
+
+
+/**
+ * Pruning the Pairs
+ */
+static void candpair_prune(struct icem *icem)
+{
+ /* The agent MUST prune the list.
+ This is done by removing a pair if its local and remote
+ candidates are identical to the local and remote candidates
+ of a pair higher up on the priority list.
+
+ NOTE: This logic assumes the list is sorted by priority
+ */
+
+ uint32_t n = ice_list_unique(&icem->checkl, unique_handler);
+ if (n > 0) {
+ DEBUG_NOTICE("%s: pruned candidate pairs: %u\n",
+ icem->name, n);
+ }
+}
+
+
+/**
+ * Computing States
+ *
+ * @param icem ICE Media object
+ */
+void ice_candpair_set_states(struct icem *icem)
+{
+ struct le *le, *le2;
+
+ /*
+ For all pairs with the same foundation, it sets the state of
+ the pair with the lowest component ID to Waiting. If there is
+ more than one such pair, the one with the highest priority is
+ used.
+ */
+
+ for (le = icem->checkl.head; le; le = le->next) {
+
+ struct ice_candpair *cp = le->data;
+
+ for (le2 = icem->checkl.head; le2; le2 = le2->next) {
+
+ struct ice_candpair *cp2 = le2->data;
+
+ if (!icem_candpair_cmp_fnd(cp, cp2))
+ continue;
+
+ if (cp2->lcand->compid < cp->lcand->compid &&
+ cp2->pprio > cp->pprio)
+ cp = cp2;
+ }
+
+ icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
+ }
+}
+
+
+/**
+ * Forming the Check Lists
+ *
+ * To form the check list for a media stream,
+ * the agent forms candidate pairs, computes a candidate pair priority,
+ * orders the pairs by priority, prunes them, and sets their states.
+ * These steps are described in this section.
+ *
+ * @param icem ICE Media object
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int icem_checklist_form(struct icem *icem)
+{
+ int err;
+
+ if (!icem)
+ return EINVAL;
+
+ if (ICE_MODE_LITE == icem->lmode) {
+ DEBUG_WARNING("%s: Checklist: only valid for full-mode\n",
+ icem->name);
+ return EINVAL;
+ }
+
+ if (!list_isempty(&icem->checkl))
+ return EALREADY;
+
+ /* 1. form candidate pairs */
+ err = candpairs_form(icem);
+ if (err)
+ return err;
+
+ /* 2. compute a candidate pair priority */
+ /* 3. order the pairs by priority */
+ icem_candpair_prio_order(&icem->checkl);
+
+ /* 4. prune the pairs */
+ candpair_prune(icem);
+
+ return err;
+}
+
+
+/* If all of the pairs in the check list are now either in the Failed or
+ Succeeded state:
+ */
+static bool iscompleted(const struct icem *icem)
+{
+ struct le *le;
+
+ for (le = icem->checkl.head; le; le = le->next) {
+
+ const struct ice_candpair *cp = le->data;
+
+ if (!icem_candpair_iscompleted(cp))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* 8. Concluding ICE Processing */
+static void concluding_ice(struct icem_comp *comp)
+{
+ struct ice_candpair *cp;
+
+ if (!comp || comp->concluded)
+ return;
+
+ /* pick the best candidate pair, highest priority */
+ cp = icem_candpair_find_st(&comp->icem->validl, comp->id,
+ ICE_CANDPAIR_SUCCEEDED);
+ if (!cp) {
+ DEBUG_WARNING("{%s.%u} conclude: no valid candpair found"
+ " (validlist=%u)\n",
+ comp->icem->name, comp->id,
+ list_count(&comp->icem->validl));
+ return;
+ }
+
+ icem_comp_set_selected(comp, cp);
+
+ if (comp->icem->conf.nom == ICE_NOMINATION_REGULAR) {
+
+ /* send STUN request with USE_CAND flag via triggered qeueue */
+ (void)icem_conncheck_send(cp, true, true);
+ icem_conncheck_schedule_check(comp->icem);
+ }
+
+ comp->concluded = true;
+}
+
+
+/**
+ * Check List and Timer State Updates
+ *
+ * @param icem ICE Media object
+ */
+void icem_checklist_update(struct icem *icem)
+{
+ struct le *le;
+ bool compl;
+ int err = 0;
+
+ compl = iscompleted(icem);
+ if (!compl)
+ return;
+
+ /*
+ * If there is not a pair in the valid list for each component of the
+ * media stream, the state of the check list is set to Failed.
+ */
+ for (le = icem->compl.head; le; le = le->next) {
+
+ struct icem_comp *comp = le->data;
+
+ if (!icem_candpair_find_compid(&icem->validl, comp->id)) {
+ DEBUG_WARNING("{%s.%u} no valid candidate pair"
+ " (validlist=%u)\n",
+ icem->name, comp->id,
+ list_count(&icem->validl));
+ err = ENOENT;
+ break;
+ }
+
+ concluding_ice(comp);
+
+ if (!comp->cp_sel)
+ continue;
+
+ icem_comp_keepalive(comp, true);
+ }
+
+ icem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED;
+
+ if (icem->chkh) {
+ icem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING,
+ icem->arg);
+ }
+}
+
+
+/**
+ * Get the Local address of the Selected Candidate pair, if available
+ *
+ * @param icem ICE Media object
+ * @param compid Component ID
+ *
+ * @return Local address if available, otherwise NULL
+ */
+const struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid)
+{
+ const struct ice_cand *cand = icem_selected_lcand(icem, compid);
+ return icem_lcand_addr(cand);
+}
+
+
+/**
+ * Get the Local candidate of the Selected Candidate pair, if available
+ *
+ * @param icem ICE Media object
+ * @param compid Component ID
+ *
+ * @return Local candidate if available, otherwise NULL
+ */
+const struct ice_cand *icem_selected_lcand(const struct icem *icem,
+ unsigned compid)
+{
+ const struct icem_comp *comp = icem_comp_find(icem, compid);
+ if (!comp || !comp->cp_sel)
+ return NULL;
+
+ return comp->cp_sel->lcand;
+}
+
+
+/**
+ * Get the Remote candidate of the Selected Candidate pair, if available
+ *
+ * @param icem ICE Media object
+ * @param compid Component ID
+ *
+ * @return Remote candidate if available, otherwise NULL
+ */
+const struct ice_cand *icem_selected_rcand(const struct icem *icem,
+ unsigned compid)
+{
+ const struct icem_comp *comp = icem_comp_find(icem, compid);
+ if (!comp || !comp->cp_sel)
+ return NULL;
+
+ return comp->cp_sel->rcand;
+}