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(lws_route_t * rou)38 _lws_routing_entry_dump(lws_route_t *rou)
39 {
40 	char sa[48], fin[192], *end = &fin[sizeof(fin)];
41 
42 	if (rou->dest.sa4.sin_family) {
43 		lws_sa46_write_numeric_address(&rou->dest, sa, sizeof(sa));
44 		lws_snprintf(fin, lws_ptr_diff_size_t(end, fin),
45 				  "dst: %s/%d, ", sa, rou->dest_len);
46 	}
47 
48 	if (rou->src.sa4.sin_family) {
49 		lws_sa46_write_numeric_address(&rou->src, sa, sizeof(sa));
50 		lws_snprintf(fin, lws_ptr_diff_size_t(end, fin),
51 				  "src: %s/%d, ", sa, rou->src_len);
52 	}
53 
54 	if (rou->gateway.sa4.sin_family) {
55 		lws_sa46_write_numeric_address(&rou->gateway, sa, sizeof(sa));
56 		lws_snprintf(fin, lws_ptr_diff_size_t(end, fin),
57 				  "gw: %s, ", sa);
58 	}
59 
60 	lwsl_info(" %s ifidx: %d, pri: %d, proto: %d\n", fin,
61 		  rou->if_idx, rou->priority, rou->proto);
62 }
63 
64 void
_lws_routing_table_dump(struct lws_context * cx)65 _lws_routing_table_dump(struct lws_context *cx)
66 {
67 	lwsl_info("%s\n", __func__);
68 	lws_start_foreach_dll(struct lws_dll2 *, d,
69 			      lws_dll2_get_head(&cx->routing_table)) {
70 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
71 
72 		_lws_routing_entry_dump(rou);
73 	} lws_end_foreach_dll(d);
74 }
75 #endif
76 
77 /*
78  * We will provide a "fingerprint ordinal" as the route uidx that is unique in
79  * the routing table.  Wsi that connect mark themselves with the uidx of the
80  * route they are estimated to be using.
81  *
82  * This lets us detect things like gw changes, eg when switching from wlan to
83  * lte there may still be a valid gateway route, but all existing tcp
84  * connections previously using the wlan gateway will be broken, since their
85  * connections are from its gateway to the peer.
86  *
87  * So when we take down a route, we take care to look for any wsi that was
88  * estimated to be using that route, eg, for gateway, and close those wsi.
89  *
90  * It's OK if the route uidx wraps, we explicitly confirm nobody else is using
91  * the uidx before assigning one to a new route.
92  *
93  * We won't use uidx 0, so it can be understood to mean the uidx was never set.
94  */
95 
96 lws_route_uidx_t
_lws_route_get_uidx(struct lws_context * cx)97 _lws_route_get_uidx(struct lws_context *cx)
98 {
99 	if (!cx->route_uidx)
100 		cx->route_uidx++;
101 
102 	while (1) {
103 		char again = 0;
104 
105 		/* Anybody in the table already uses the pt's next uidx? */
106 
107 		lws_start_foreach_dll(struct lws_dll2 *, d,
108 				      lws_dll2_get_head(&cx->routing_table)) {
109 			lws_route_t *rou = lws_container_of(d, lws_route_t, list);
110 
111 			if (rou->uidx == cx->route_uidx) {
112 				/* if so, bump and restart the check */
113 				cx->route_uidx++;
114 				if (!cx->route_uidx)
115 					cx->route_uidx++;
116 				if (again) {
117 					assert(0); /* we have filled up the 8-bit uidx space? */
118 					return 0;
119 				}
120 				again = 1;
121 			}
122 		} lws_end_foreach_dll(d);
123 
124 		if (!again)
125 			return cx->route_uidx++;
126 	}
127 }
128 
129 lws_route_t *
_lws_route_remove(struct lws_context_per_thread * pt,lws_route_t * robj,int flags)130 _lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags)
131 {
132 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
133 			      lws_dll2_get_head(&pt->context->routing_table)) {
134 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
135 
136 		if ((!(flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->src, &rou->src)) &&
137 		    ((flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->dest, &rou->dest)) &&
138 		    (!robj->gateway.sa4.sin_family ||
139 		     !lws_sa46_compare_ads(&robj->gateway, &rou->gateway)) &&
140 		    robj->dest_len <= rou->dest_len &&
141 		    robj->if_idx == rou->if_idx &&
142 		    ((flags & LRR_IGNORE_PRI) ||
143 		      robj->priority == rou->priority)
144 		    ) {
145 			if (flags & LRR_JUST_CHECK)
146 				return rou;
147 			lwsl_info("%s: deleting route\n", __func__);
148 			_lws_route_pt_close_route_users(pt, robj->uidx);
149 			lws_dll2_remove(&rou->list);
150 			lws_free(rou);
151 		}
152 
153 	} lws_end_foreach_dll_safe(d, d1);
154 
155 	return NULL;
156 }
157 
158 void
_lws_route_table_empty(struct lws_context_per_thread * pt)159 _lws_route_table_empty(struct lws_context_per_thread *pt)
160 {
161 
162 	if (!pt->context)
163 		return;
164 
165 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
166 				   lws_dll2_get_head(&pt->context->routing_table)) {
167 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
168 
169 		lws_dll2_remove(&rou->list);
170 		lws_free(rou);
171 
172 	} lws_end_foreach_dll_safe(d, d1);
173 }
174 
175 void
_lws_route_table_ifdown(struct lws_context_per_thread * pt,int idx)176 _lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx)
177 {
178 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
179 				   lws_dll2_get_head(&pt->context->routing_table)) {
180 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
181 
182 		if (rou->if_idx == idx) {
183 			lws_dll2_remove(&rou->list);
184 			lws_free(rou);
185 		}
186 
187 	} lws_end_foreach_dll_safe(d, d1);
188 }
189 
190 lws_route_t *
_lws_route_est_outgoing(struct lws_context_per_thread * pt,const lws_sockaddr46 * dest)191 _lws_route_est_outgoing(struct lws_context_per_thread *pt,
192 		        const lws_sockaddr46 *dest)
193 {
194 	lws_route_t *best_gw = NULL;
195 	int best_gw_priority = INT_MAX;
196 
197 	if (!dest->sa4.sin_family) {
198 		lwsl_notice("%s: dest has 0 AF\n", __func__);
199 		/* leave it alone */
200 		return NULL;
201 	}
202 
203 	/*
204 	 * Given the dest address and the current routing table, select the
205 	 * route we think it would go out on... if we find a matching network
206 	 * route, just return that, otherwise find the "best" gateway by
207 	 * looking at the priority of them.
208 	 */
209 
210 	lws_start_foreach_dll(struct lws_dll2 *, d,
211 			      lws_dll2_get_head(&pt->context->routing_table)) {
212 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
213 
214 		// _lws_routing_entry_dump(rou);
215 
216 		if (rou->dest.sa4.sin_family &&
217 		    !lws_sa46_on_net(dest, &rou->dest, rou->dest_len))
218 			/*
219 			 * Yes, he has a matching network route, it beats out
220 			 * any gateway route.  This is like finding a route for
221 			 * 192.168.0.0/24 when dest is 192.168.0.1.
222 			 */
223 			return rou;
224 
225 		lwsl_debug("%s: dest af %d, rou gw af %d, pri %d\n", __func__,
226 			   dest->sa4.sin_family, rou->gateway.sa4.sin_family,
227 			   rou->priority);
228 
229 		if (rou->gateway.sa4.sin_family &&
230 
231 			/*
232 			 *  dest  gw
233 			 *   4     4    OK
234 			 *   4     6    OK with ::ffff:x:x
235 			 *   6     4    not supported directly
236 			 *   6     6    OK
237 			 */
238 
239 		    (dest->sa4.sin_family == rou->gateway.sa4.sin_family ||
240 			(dest->sa4.sin_family == AF_INET &&
241 			 rou->gateway.sa4.sin_family == AF_INET6)) &&
242 		    rou->priority < best_gw_priority) {
243 			lwsl_info("%s: gw hit\n", __func__);
244 			best_gw_priority = rou->priority;
245 			best_gw = rou;
246 		}
247 
248 	} lws_end_foreach_dll(d);
249 
250 	/*
251 	 * Either best_gw is the best gw route and we set *best_gw_priority to
252 	 * the best one's priority, or we're returning NULL as no network or
253 	 * gw route for dest.
254 	 */
255 
256 	lwsl_info("%s: returning %p\n", __func__, best_gw);
257 
258 	return best_gw;
259 }
260 
261 /*
262  * Determine if the source still exists
263  */
264 
265 lws_route_t *
_lws_route_find_source(struct lws_context_per_thread * pt,const lws_sockaddr46 * src)266 _lws_route_find_source(struct lws_context_per_thread *pt,
267 		       const lws_sockaddr46 *src)
268 {
269 	lws_start_foreach_dll(struct lws_dll2 *, d,
270 			      lws_dll2_get_head(&pt->context->routing_table)) {
271 		lws_route_t *rou = lws_container_of(d, lws_route_t, list);
272 
273 		// _lws_routing_entry_dump(rou);
274 
275 		if (rou->src.sa4.sin_family &&
276 		    !lws_sa46_compare_ads(src, &rou->src))
277 			/*
278 			 * Source route still exists
279 			 */
280 			return rou;
281 
282 	} lws_end_foreach_dll(d);
283 
284 	return NULL;
285 }
286 
287 int
_lws_route_check_wsi(struct lws * wsi)288 _lws_route_check_wsi(struct lws *wsi)
289 {
290 	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
291 	char buf[72];
292 
293 	if (!wsi->sa46_peer.sa4.sin_family ||
294 #if defined(LWS_WITH_UNIX_SOCK)
295 	     wsi->unix_skt ||
296 	     wsi->sa46_peer.sa4.sin_family == AF_UNIX ||
297 #endif
298 	    wsi->desc.sockfd == LWS_SOCK_INVALID)
299 		/* not a socket, cannot judge by route, or not connected,
300 		 * leave it alone */
301 		return 0; /* OK */
302 
303 	/* the route to the peer is still workable? */
304 
305 	if (!_lws_route_est_outgoing(pt, &wsi->sa46_peer)) {
306 		/* no way to talk to the peer */
307 		lwsl_notice("%s: %s: dest route gone\n", __func__, wsi->lc.gutag);
308 		return 1;
309 	}
310 
311 	/* the source address is still workable? */
312 
313 	lws_sa46_write_numeric_address(&wsi->sa46_local,
314 				       buf, sizeof(buf));
315 	//lwsl_notice("%s: %s sa46_local %s fam %d\n", __func__, wsi->lc.gutag,
316 	//		buf, wsi->sa46_local.sa4.sin_family);
317 
318 	if (wsi->sa46_local.sa4.sin_family &&
319 	    !_lws_route_find_source(pt, &wsi->sa46_local)) {
320 
321 		lws_sa46_write_numeric_address(&wsi->sa46_local,
322 					       buf, sizeof(buf));
323 		lwsl_notice("%s: %s: source %s gone\n", __func__,
324 			    wsi->lc.gutag, buf);
325 
326 		return 1;
327 	}
328 
329 	lwsl_debug("%s: %s: source + dest OK\n", __func__, wsi->lc.gutag);
330 
331 	return 0;
332 }
333 
334 int
_lws_route_pt_close_unroutable(struct lws_context_per_thread * pt)335 _lws_route_pt_close_unroutable(struct lws_context_per_thread *pt)
336 {
337 	struct lws *wsi;
338 	unsigned int n;
339 
340 	if (!pt->context->nl_initial_done ||
341 	    pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG)
342 		return 0;
343 
344 	lwsl_debug("%s\n", __func__);
345 #if defined(_DEBUG)
346 	_lws_routing_table_dump(pt->context);
347 #endif
348 
349 	for (n = 0; n < pt->fds_count; n++) {
350 		wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
351 		if (!wsi)
352 			continue;
353 
354 		if (_lws_route_check_wsi(wsi)) {
355 			lwsl_info("%s: culling wsi %s\n", __func__, lws_wsi_tag(wsi));
356 			lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
357 		}
358 	}
359 
360 	return 0;
361 }
362 
363 int
_lws_route_pt_close_route_users(struct lws_context_per_thread * pt,lws_route_uidx_t uidx)364 _lws_route_pt_close_route_users(struct lws_context_per_thread *pt,
365 			       lws_route_uidx_t uidx)
366 {
367 	struct lws *wsi;
368 	unsigned int n;
369 
370 	if (!uidx)
371 		return 0;
372 
373 	lwsl_info("%s: closing users of route %d\n", __func__, uidx);
374 
375 	for (n = 0; n < pt->fds_count; n++) {
376 		wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
377 		if (!wsi)
378 			continue;
379 
380 		if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
381 #if defined(LWS_WITH_UNIX_SOCK)
382 		    !wsi->unix_skt &&
383 		    wsi->sa46_peer.sa4.sin_family != AF_UNIX &&
384 #endif
385 		    wsi->sa46_peer.sa4.sin_family &&
386 		    wsi->peer_route_uidx == uidx) {
387 			lwsl_notice("%s: culling wsi %s\n", __func__, lws_wsi_tag(wsi));
388 			lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
389 		}
390 	}
391 
392 	return 0;
393 }
394