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
37void
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
71void
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
103lws_route_uidx_t
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
141lws_route_t *
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
168void
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
185void
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
200lws_route_t *
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
275lws_route_t *
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
297int
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
343int
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
376int
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