1 /**
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  */
candpairs_form(struct icem * icem)27 static 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 */
cand_srflx_addr(const struct ice_cand * c)66 static 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 */
unique_handler(struct le * le1,struct le * le2)73 static 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  */
candpair_prune(struct icem * icem)92 static 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  */
ice_candpair_set_states(struct icem * icem)115 void 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  */
icem_checklist_form(struct icem * icem)159 int 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  */
iscompleted(const struct icem * icem)194 static 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 */
concluding_ice(struct icem_comp * comp)211 static 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  */
icem_checklist_update(struct icem * icem)247 void 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  */
icem_selected_laddr(const struct icem * icem,unsigned compid)299 const 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  */
icem_selected_lcand(const struct icem * icem,unsigned compid)314 const 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  */
icem_selected_rcand(const struct icem * icem,unsigned compid)333 const 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 }
342