blob: 24e4f343dce83b531da3db46e359ba8bcd4543d4 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file chklist.c ICE Checklist
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_list.h>
12#include <re_tmr.h>
13#include <re_sa.h>
14#include <re_stun.h>
15#include <re_ice.h>
16#include "ice.h"
17
18
19#define DEBUG_MODULE "chklist"
20#define DEBUG_LEVEL 5
21#include <re_dbg.h>
22
23
24/**
25 * Forming Candidate Pairs
26 */
27static int candpairs_form(struct icem *icem)
28{
29 struct le *le;
30 int err = 0;
31
32 if (list_isempty(&icem->lcandl))
33 return ENOENT;
34
35 if (list_isempty(&icem->rcandl)) {
36 DEBUG_WARNING("%s: no remote candidates\n", icem->name);
37 return ENOENT;
38 }
39
40 for (le = icem->lcandl.head; le; le = le->next) {
41
42 struct ice_cand *lcand = le->data;
43 struct le *rle;
44
45 for (rle = icem->rcandl.head; rle; rle = rle->next) {
46
47 struct ice_cand *rcand = rle->data;
48
49 if (lcand->compid != rcand->compid)
50 continue;
51
52 if (sa_af(&lcand->addr) != sa_af(&rcand->addr))
53 continue;
54
55 err = icem_candpair_alloc(NULL, icem, lcand, rcand);
56 if (err)
57 return err;
58 }
59 }
60
61 return err;
62}
63
64
65/* Replace server reflexive candidates by its base */
66static const struct sa *cand_srflx_addr(const struct ice_cand *c)
67{
68 return (ICE_CAND_TYPE_SRFLX == c->type) ? &c->base->addr : &c->addr;
69}
70
71
72/* return: NULL to keep, pointer to remove object */
73static void *unique_handler(struct le *le1, struct le *le2)
74{
75 struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;
76
77 if (cp1->comp->id != cp2->comp->id)
78 return NULL;
79
80 if (!sa_cmp(cand_srflx_addr(cp1->lcand),
81 cand_srflx_addr(cp2->lcand), SA_ALL) ||
82 !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL))
83 return NULL;
84
85 return cp1->pprio < cp2->pprio ? cp1 : cp2;
86}
87
88
89/**
90 * Pruning the Pairs
91 */
92static void candpair_prune(struct icem *icem)
93{
94 /* The agent MUST prune the list.
95 This is done by removing a pair if its local and remote
96 candidates are identical to the local and remote candidates
97 of a pair higher up on the priority list.
98
99 NOTE: This logic assumes the list is sorted by priority
100 */
101
102 uint32_t n = ice_list_unique(&icem->checkl, unique_handler);
103 if (n > 0) {
104 DEBUG_NOTICE("%s: pruned candidate pairs: %u\n",
105 icem->name, n);
106 }
107}
108
109
110/**
111 * Computing States
112 *
113 * @param icem ICE Media object
114 */
115void ice_candpair_set_states(struct icem *icem)
116{
117 struct le *le, *le2;
118
119 /*
120 For all pairs with the same foundation, it sets the state of
121 the pair with the lowest component ID to Waiting. If there is
122 more than one such pair, the one with the highest priority is
123 used.
124 */
125
126 for (le = icem->checkl.head; le; le = le->next) {
127
128 struct ice_candpair *cp = le->data;
129
130 for (le2 = icem->checkl.head; le2; le2 = le2->next) {
131
132 struct ice_candpair *cp2 = le2->data;
133
134 if (!icem_candpair_cmp_fnd(cp, cp2))
135 continue;
136
137 if (cp2->lcand->compid < cp->lcand->compid &&
138 cp2->pprio > cp->pprio)
139 cp = cp2;
140 }
141
142 icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
143 }
144}
145
146
147/**
148 * Forming the Check Lists
149 *
150 * To form the check list for a media stream,
151 * the agent forms candidate pairs, computes a candidate pair priority,
152 * orders the pairs by priority, prunes them, and sets their states.
153 * These steps are described in this section.
154 *
155 * @param icem ICE Media object
156 *
157 * @return 0 if success, otherwise errorcode
158 */
159int icem_checklist_form(struct icem *icem)
160{
161 int err;
162
163 if (!icem)
164 return EINVAL;
165
166 if (ICE_MODE_LITE == icem->lmode) {
167 DEBUG_WARNING("%s: Checklist: only valid for full-mode\n",
168 icem->name);
169 return EINVAL;
170 }
171
172 if (!list_isempty(&icem->checkl))
173 return EALREADY;
174
175 /* 1. form candidate pairs */
176 err = candpairs_form(icem);
177 if (err)
178 return err;
179
180 /* 2. compute a candidate pair priority */
181 /* 3. order the pairs by priority */
182 icem_candpair_prio_order(&icem->checkl);
183
184 /* 4. prune the pairs */
185 candpair_prune(icem);
186
187 return err;
188}
189
190
191/* If all of the pairs in the check list are now either in the Failed or
192 Succeeded state:
193 */
194static bool iscompleted(const struct icem *icem)
195{
196 struct le *le;
197
198 for (le = icem->checkl.head; le; le = le->next) {
199
200 const struct ice_candpair *cp = le->data;
201
202 if (!icem_candpair_iscompleted(cp))
203 return false;
204 }
205
206 return true;
207}
208
209
210/* 8. Concluding ICE Processing */
211static void concluding_ice(struct icem_comp *comp)
212{
213 struct ice_candpair *cp;
214
215 if (!comp || comp->concluded)
216 return;
217
218 /* pick the best candidate pair, highest priority */
219 cp = icem_candpair_find_st(&comp->icem->validl, comp->id,
220 ICE_CANDPAIR_SUCCEEDED);
221 if (!cp) {
222 DEBUG_WARNING("{%s.%u} conclude: no valid candpair found"
223 " (validlist=%u)\n",
224 comp->icem->name, comp->id,
225 list_count(&comp->icem->validl));
226 return;
227 }
228
229 icem_comp_set_selected(comp, cp);
230
231 if (comp->icem->conf.nom == ICE_NOMINATION_REGULAR) {
232
233 /* send STUN request with USE_CAND flag via triggered qeueue */
234 (void)icem_conncheck_send(cp, true, true);
235 icem_conncheck_schedule_check(comp->icem);
236 }
237
238 comp->concluded = true;
239}
240
241
242/**
243 * Check List and Timer State Updates
244 *
245 * @param icem ICE Media object
246 */
247void icem_checklist_update(struct icem *icem)
248{
249 struct le *le;
250 bool compl;
251 int err = 0;
252
253 compl = iscompleted(icem);
254 if (!compl)
255 return;
256
257 /*
258 * If there is not a pair in the valid list for each component of the
259 * media stream, the state of the check list is set to Failed.
260 */
261 for (le = icem->compl.head; le; le = le->next) {
262
263 struct icem_comp *comp = le->data;
264
265 if (!icem_candpair_find_compid(&icem->validl, comp->id)) {
266 DEBUG_WARNING("{%s.%u} no valid candidate pair"
267 " (validlist=%u)\n",
268 icem->name, comp->id,
269 list_count(&icem->validl));
270 err = ENOENT;
271 break;
272 }
273
274 concluding_ice(comp);
275
276 if (!comp->cp_sel)
277 continue;
278
279 icem_comp_keepalive(comp, true);
280 }
281
282 icem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED;
283
284 if (icem->chkh) {
285 icem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING,
286 icem->arg);
287 }
288}
289
290
291/**
292 * Get the Local address of the Selected Candidate pair, if available
293 *
294 * @param icem ICE Media object
295 * @param compid Component ID
296 *
297 * @return Local address if available, otherwise NULL
298 */
299const struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid)
300{
301 const struct ice_cand *cand = icem_selected_lcand(icem, compid);
302 return icem_lcand_addr(cand);
303}
304
305
306/**
307 * Get the Local candidate of the Selected Candidate pair, if available
308 *
309 * @param icem ICE Media object
310 * @param compid Component ID
311 *
312 * @return Local candidate if available, otherwise NULL
313 */
314const struct ice_cand *icem_selected_lcand(const struct icem *icem,
315 unsigned compid)
316{
317 const struct icem_comp *comp = icem_comp_find(icem, compid);
318 if (!comp || !comp->cp_sel)
319 return NULL;
320
321 return comp->cp_sel->lcand;
322}
323
324
325/**
326 * Get the Remote candidate of the Selected Candidate pair, if available
327 *
328 * @param icem ICE Media object
329 * @param compid Component ID
330 *
331 * @return Remote candidate if available, otherwise NULL
332 */
333const struct ice_cand *icem_selected_rcand(const struct icem *icem,
334 unsigned compid)
335{
336 const struct icem_comp *comp = icem_comp_find(icem, compid);
337 if (!comp || !comp->cp_sel)
338 return NULL;
339
340 return comp->cp_sel->rcand;
341}