• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  *
24  * We mainly focus on the routing table / gateways because those are the
25  * elements that decide if we can get on to the internet or not.
26  *
27  * Everything here is _ because the caller needs to hold the pt lock in order
28  * to access the pt routing table safely
29  */
30 
31 #include <private-lib-core.h>
32 
33 #if defined(_DEBUG)
34 
35 
36 
37 void
_lws_routing_entry_dump(struct lws_context * cx,lws_route_t * rou)38 _lws_routing_entry_dump(struct lws_context *cx, lws_route_t *rou)
39 {
40 	char sa[48], fin[192], *end = &fin[sizeof(fin)];
41 	char *it = fin;
42 	int n;
43 
44 	fin[0] = '\0';
45 
46 	if (rou->dest.sa4.sin_family) {
47 		lws_sa46_write_numeric_address(&rou->dest, sa, sizeof(sa));
48 		n = lws_snprintf(it, lws_ptr_diff_size_t(end, it),
49 				  "dst: %s/%d, ", sa, rou->dest_len);
50 		it = it + n;
51 	}
52 
53 	if (rou->src.sa4.sin_family) {
54 		lws_sa46_write_numeric_address(&rou->src, sa, sizeof(sa));
55 		n = lws_snprintf(it, lws_ptr_diff_size_t(end, it),
56 				  "src: %s/%d, ", sa, rou->src_len);
57 		it = it + n;
58 	}
59 
60 	if (rou->gateway.sa4.sin_family) {
61 		lws_sa46_write_numeric_address(&rou->gateway, sa, sizeof(sa));
62 		n = lws_snprintf(it, lws_ptr_diff_size_t(end, it),
63 				  "gw: %s, ", sa);
64 		it = it + n;
65 	}
66 
67 	lwsl_cx_info(cx, " %s ifidx: %d, pri: %d, proto: %d\n", fin,
68 		  rou->if_idx, rou->priority, rou->proto);
69 }
70 
71 void
_lws_routing_table_dump(struct lws_context * cx)72 _lws_routing_table_dump(struct lws_context *cx)
73 {
74 	lwsl_cx_info(cx, "\n");
75 	lws_start_foreach_dll(struct lws_dll2 *, d,
76 			      lws_dll2_get_head(&cx->routing_table)) {
77 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
78 
79 		_lws_routing_entry_dump(cx, rou);
80 	} lws_end_foreach_dll(d);
81 }
82 #endif
83 
84 /*
85  * We will provide a "fingerprint ordinal" as the route uidx that is unique in
86  * the routing table.  Wsi that connect mark themselves with the uidx of the
87  * route they are estimated to be using.
88  *
89  * This lets us detect things like gw changes, eg when switching from wlan to
90  * lte there may still be a valid gateway route, but all existing tcp
91  * connections previously using the wlan gateway will be broken, since their
92  * connections are from its gateway to the peer.
93  *
94  * So when we take down a route, we take care to look for any wsi that was
95  * estimated to be using that route, eg, for gateway, and close those wsi.
96  *
97  * It's OK if the route uidx wraps, we explicitly confirm nobody else is using
98  * the uidx before assigning one to a new route.
99  *
100  * We won't use uidx 0, so it can be understood to mean the uidx was never set.
101  */
102 
103 lws_route_uidx_t
_lws_route_get_uidx(struct lws_context * cx)104 _lws_route_get_uidx(struct lws_context *cx)
105 {
106 	lws_route_uidx_t ou;
107 
108 	if (!cx->route_uidx)
109 		cx->route_uidx++;
110 
111 	ou = cx->route_uidx;
112 
113 	do {
114 		uint8_t again = 0;
115 
116 		/* Anybody in the table already uses the pt's next uidx? */
117 
118 		lws_start_foreach_dll(struct lws_dll2 *, d,
119 				      lws_dll2_get_head(&cx->routing_table)) {
120 			lws_route_t *rou = lws_container_of(d, lws_route_t, list);
121 
122 			if (rou->uidx == cx->route_uidx) {
123 				/* if so, bump and restart the check */
124 				cx->route_uidx++;
125 				if (!cx->route_uidx)
126 					cx->route_uidx++;
127 				if (cx->route_uidx == ou) {
128 					assert(0); /* we have filled up the 8-bit uidx space? */
129 					return 0;
130 				}
131 				again = 1;
132 				break;
133 			}
134 		} lws_end_foreach_dll(d);
135 
136 		if (!again)
137 			return cx->route_uidx++;
138 	} while (1);
139 }
140 
141 lws_route_t *
_lws_route_remove(struct lws_context_per_thread * pt,lws_route_t * robj,int flags)142 _lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags)
143 {
144 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
145 			      lws_dll2_get_head(&pt->context->routing_table)) {
146 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
147 
148 		if ((!(flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->src, &rou->src)) &&
149 		    (!(flags & LRR_MATCH_DST) || !lws_sa46_compare_ads(&robj->dest, &rou->dest)) &&
150 		    (!robj->gateway.sa4.sin_family ||
151 		     !lws_sa46_compare_ads(&robj->gateway, &rou->gateway)) &&
152 		    robj->dest_len <= rou->dest_len &&
153 		    robj->if_idx == rou->if_idx &&
154 		    ((flags & LRR_IGNORE_PRI) ||
155 		      robj->priority == rou->priority)
156 		    ) {
157 			lwsl_cx_info(pt->context, "deleting route");
158 			_lws_route_pt_close_route_users(pt, robj->uidx);
159 			lws_dll2_remove(&rou->list);
160 			lws_free(rou);
161 		}
162 
163 	} lws_end_foreach_dll_safe(d, d1);
164 
165 	return NULL;
166 }
167 
168 void
_lws_route_table_empty(struct lws_context_per_thread * pt)169 _lws_route_table_empty(struct lws_context_per_thread *pt)
170 {
171 
172 	if (!pt->context)
173 		return;
174 
175 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
176 				   lws_dll2_get_head(&pt->context->routing_table)) {
177 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
178 
179 		lws_dll2_remove(&rou->list);
180 		lws_free(rou);
181 
182 	} lws_end_foreach_dll_safe(d, d1);
183 }
184 
185 void
_lws_route_table_ifdown(struct lws_context_per_thread * pt,int idx)186 _lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx)
187 {
188 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
189 				   lws_dll2_get_head(&pt->context->routing_table)) {
190 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
191 
192 		if (rou->if_idx == idx) {
193 			lws_dll2_remove(&rou->list);
194 			lws_free(rou);
195 		}
196 
197 	} lws_end_foreach_dll_safe(d, d1);
198 }
199 
200 lws_route_t *
_lws_route_est_outgoing(struct lws_context_per_thread * pt,const lws_sockaddr46 * dest)201 _lws_route_est_outgoing(struct lws_context_per_thread *pt,
202 		        const lws_sockaddr46 *dest)
203 {
204 	lws_route_t *best_gw = NULL;
205 	int best_gw_priority = INT_MAX;
206 
207 	if (!dest->sa4.sin_family) {
208 		lwsl_cx_notice(pt->context, "dest has 0 AF");
209 		/* leave it alone */
210 		return NULL;
211 	}
212 
213 	/*
214 	 * Given the dest address and the current routing table, select the
215 	 * route we think it would go out on... if we find a matching network
216 	 * route, just return that, otherwise find the "best" gateway by
217 	 * looking at the priority of them.
218 	 */
219 
220 	lws_start_foreach_dll(struct lws_dll2 *, d,
221 			      lws_dll2_get_head(&pt->context->routing_table)) {
222 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
223 
224 		// _lws_routing_entry_dump(rou);
225 
226 		if (rou->dest.sa4.sin_family &&
227 		    !lws_sa46_on_net(dest, &rou->dest, rou->dest_len))
228 			/*
229 			 * Yes, he has a matching network route, it beats out
230 			 * any gateway route.  This is like finding a route for
231 			 * 192.168.0.0/24 when dest is 192.168.0.1.
232 			 */
233 			return rou;
234 
235 		lwsl_cx_debug(pt->context, "dest af %d, rou gw af %d, pri %d",
236 			      dest->sa4.sin_family, rou->gateway.sa4.sin_family,
237 			      rou->priority);
238 
239 		if (rou->gateway.sa4.sin_family &&
240 
241 			/*
242 			 *  dest  gw
243 			 *   4     4    OK
244 			 *   4     6    OK with ::ffff:x:x
245 			 *   6     4    not supported directly
246 			 *   6     6    OK
247 			 */
248 
249 		    (dest->sa4.sin_family == rou->gateway.sa4.sin_family ||
250 			(dest->sa4.sin_family == AF_INET &&
251 			 rou->gateway.sa4.sin_family == AF_INET6)) &&
252 		    rou->priority < best_gw_priority) {
253 			lwsl_cx_info(pt->context, "gw hit");
254 			best_gw_priority = rou->priority;
255 			best_gw = rou;
256 		}
257 
258 	} lws_end_foreach_dll(d);
259 
260 	/*
261 	 * Either best_gw is the best gw route and we set *best_gw_priority to
262 	 * the best one's priority, or we're returning NULL as no network or
263 	 * gw route for dest.
264 	 */
265 
266 	lwsl_cx_info(pt->context, "returning %p", best_gw);
267 
268 	return best_gw;
269 }
270 
271 /*
272  * Determine if the source still exists
273  */
274 
275 lws_route_t *
_lws_route_find_source(struct lws_context_per_thread * pt,const lws_sockaddr46 * src)276 _lws_route_find_source(struct lws_context_per_thread *pt,
277 		       const lws_sockaddr46 *src)
278 {
279 	lws_start_foreach_dll(struct lws_dll2 *, d,
280 			      lws_dll2_get_head(&pt->context->routing_table)) {
281 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
282 
283 		// _lws_routing_entry_dump(rou);
284 
285 		if (rou->src.sa4.sin_family &&
286 		    !lws_sa46_compare_ads(src, &rou->src))
287 			/*
288 			 * Source route still exists
289 			 */
290 			return rou;
291 
292 	} lws_end_foreach_dll(d);
293 
294 	return NULL;
295 }
296 
297 int
_lws_route_check_wsi(struct lws * wsi)298 _lws_route_check_wsi(struct lws *wsi)
299 {
300 	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
301 	char buf[72];
302 
303 	if (!wsi->sa46_peer.sa4.sin_family ||
304 #if defined(LWS_WITH_UNIX_SOCK)
305 	     wsi->unix_skt ||
306 	     wsi->sa46_peer.sa4.sin_family == AF_UNIX ||
307 #endif
308 	    wsi->desc.sockfd == LWS_SOCK_INVALID)
309 		/* not a socket, cannot judge by route, or not connected,
310 		 * leave it alone */
311 		return 0; /* OK */
312 
313 	/* the route to the peer is still workable? */
314 
315 	if (!_lws_route_est_outgoing(pt, &wsi->sa46_peer)) {
316 		/* no way to talk to the peer */
317 		lwsl_wsi_notice(wsi, "dest route gone");
318 		return 1;
319 	}
320 
321 	/* the source address is still workable? */
322 
323 	lws_sa46_write_numeric_address(&wsi->sa46_local,
324 				       buf, sizeof(buf));
325 	//lwsl_notice("%s: %s sa46_local %s fam %d\n", __func__, wsi->lc.gutag,
326 	//		buf, wsi->sa46_local.sa4.sin_family);
327 
328 	if (wsi->sa46_local.sa4.sin_family &&
329 	    !_lws_route_find_source(pt, &wsi->sa46_local)) {
330 
331 		lws_sa46_write_numeric_address(&wsi->sa46_local,
332 					       buf, sizeof(buf));
333 		lwsl_wsi_notice(wsi, "source %s gone", buf);
334 
335 		return 1;
336 	}
337 
338 	lwsl_wsi_debug(wsi, "source + dest OK");
339 
340 	return 0;
341 }
342 
343 int
_lws_route_pt_close_unroutable(struct lws_context_per_thread * pt)344 _lws_route_pt_close_unroutable(struct lws_context_per_thread *pt)
345 {
346 	struct lws *wsi;
347 	unsigned int n;
348 
349 	if (!pt->context->nl_initial_done
350 #if defined(LWS_WITH_SYS_STATE)
351 		       	||
352 	    pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG
353 #endif
354 	)
355 		return 0;
356 
357 	lwsl_cx_debug(pt->context, "in");
358 #if defined(_DEBUG)
359 	_lws_routing_table_dump(pt->context);
360 #endif
361 
362 	for (n = 0; n < pt->fds_count; n++) {
363 		wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
364 		if (!wsi)
365 			continue;
366 
367 		if (_lws_route_check_wsi(wsi)) {
368 			lwsl_wsi_info(wsi, "culling wsi");
369 			lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
370 		}
371 	}
372 
373 	return 0;
374 }
375 
376 int
_lws_route_pt_close_route_users(struct lws_context_per_thread * pt,lws_route_uidx_t uidx)377 _lws_route_pt_close_route_users(struct lws_context_per_thread *pt,
378 				lws_route_uidx_t uidx)
379 {
380 	struct lws *wsi;
381 	unsigned int n;
382 
383 	if (!uidx)
384 		return 0;
385 
386 	lwsl_cx_info(pt->context, "closing users of route %d", uidx);
387 
388 	for (n = 0; n < pt->fds_count; n++) {
389 		wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
390 		if (!wsi)
391 			continue;
392 
393 		if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
394 #if defined(LWS_WITH_UNIX_SOCK)
395 		    !wsi->unix_skt &&
396 		    wsi->sa46_peer.sa4.sin_family != AF_UNIX &&
397 #endif
398 		    wsi->sa46_peer.sa4.sin_family &&
399 		    wsi->peer_route_uidx == uidx) {
400 			lwsl_wsi_notice(wsi, "culling wsi");
401 			lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
402 		}
403 	}
404 
405 	return 0;
406 }
407