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
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_cx_info(cx, " %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_cx_info(cx, "\n");
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(cx, 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 uint8_t ou;
100
101 if (!cx->route_uidx)
102 cx->route_uidx++;
103
104 ou = cx->route_uidx;
105
106 do {
107 uint8_t again = 0;
108
109 /* Anybody in the table already uses the pt's next uidx? */
110
111 lws_start_foreach_dll(struct lws_dll2 *, d,
112 lws_dll2_get_head(&cx->routing_table)) {
113 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
114
115 if (rou->uidx == cx->route_uidx) {
116 /* if so, bump and restart the check */
117 cx->route_uidx++;
118 if (!cx->route_uidx)
119 cx->route_uidx++;
120 if (cx->route_uidx == ou) {
121 assert(0); /* we have filled up the 8-bit uidx space? */
122 return 0;
123 }
124 again = 1;
125 break;
126 }
127 } lws_end_foreach_dll(d);
128
129 if (!again)
130 return cx->route_uidx++;
131 } while (1);
132 }
133
134 lws_route_t *
_lws_route_remove(struct lws_context_per_thread * pt,lws_route_t * robj,int flags)135 _lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags)
136 {
137 lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
138 lws_dll2_get_head(&pt->context->routing_table)) {
139 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
140
141 if ((!(flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->src, &rou->src)) &&
142 ((flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->dest, &rou->dest)) &&
143 (!robj->gateway.sa4.sin_family ||
144 !lws_sa46_compare_ads(&robj->gateway, &rou->gateway)) &&
145 robj->dest_len <= rou->dest_len &&
146 robj->if_idx == rou->if_idx &&
147 ((flags & LRR_IGNORE_PRI) ||
148 robj->priority == rou->priority)
149 ) {
150 if (flags & LRR_JUST_CHECK)
151 return rou;
152 lwsl_cx_info(pt->context, "deleting route");
153 _lws_route_pt_close_route_users(pt, robj->uidx);
154 lws_dll2_remove(&rou->list);
155 lws_free(rou);
156 }
157
158 } lws_end_foreach_dll_safe(d, d1);
159
160 return NULL;
161 }
162
163 void
_lws_route_table_empty(struct lws_context_per_thread * pt)164 _lws_route_table_empty(struct lws_context_per_thread *pt)
165 {
166
167 if (!pt->context)
168 return;
169
170 lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
171 lws_dll2_get_head(&pt->context->routing_table)) {
172 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
173
174 lws_dll2_remove(&rou->list);
175 lws_free(rou);
176
177 } lws_end_foreach_dll_safe(d, d1);
178 }
179
180 void
_lws_route_table_ifdown(struct lws_context_per_thread * pt,int idx)181 _lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx)
182 {
183 lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
184 lws_dll2_get_head(&pt->context->routing_table)) {
185 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
186
187 if (rou->if_idx == idx) {
188 lws_dll2_remove(&rou->list);
189 lws_free(rou);
190 }
191
192 } lws_end_foreach_dll_safe(d, d1);
193 }
194
195 lws_route_t *
_lws_route_est_outgoing(struct lws_context_per_thread * pt,const lws_sockaddr46 * dest)196 _lws_route_est_outgoing(struct lws_context_per_thread *pt,
197 const lws_sockaddr46 *dest)
198 {
199 lws_route_t *best_gw = NULL;
200 int best_gw_priority = INT_MAX;
201
202 if (!dest->sa4.sin_family) {
203 lwsl_cx_notice(pt->context, "dest has 0 AF");
204 /* leave it alone */
205 return NULL;
206 }
207
208 /*
209 * Given the dest address and the current routing table, select the
210 * route we think it would go out on... if we find a matching network
211 * route, just return that, otherwise find the "best" gateway by
212 * looking at the priority of them.
213 */
214
215 lws_start_foreach_dll(struct lws_dll2 *, d,
216 lws_dll2_get_head(&pt->context->routing_table)) {
217 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
218
219 // _lws_routing_entry_dump(rou);
220
221 if (rou->dest.sa4.sin_family &&
222 !lws_sa46_on_net(dest, &rou->dest, rou->dest_len))
223 /*
224 * Yes, he has a matching network route, it beats out
225 * any gateway route. This is like finding a route for
226 * 192.168.0.0/24 when dest is 192.168.0.1.
227 */
228 return rou;
229
230 lwsl_cx_debug(pt->context, "dest af %d, rou gw af %d, pri %d",
231 dest->sa4.sin_family, rou->gateway.sa4.sin_family,
232 rou->priority);
233
234 if (rou->gateway.sa4.sin_family &&
235
236 /*
237 * dest gw
238 * 4 4 OK
239 * 4 6 OK with ::ffff:x:x
240 * 6 4 not supported directly
241 * 6 6 OK
242 */
243
244 (dest->sa4.sin_family == rou->gateway.sa4.sin_family ||
245 (dest->sa4.sin_family == AF_INET &&
246 rou->gateway.sa4.sin_family == AF_INET6)) &&
247 rou->priority < best_gw_priority) {
248 lwsl_cx_info(pt->context, "gw hit");
249 best_gw_priority = rou->priority;
250 best_gw = rou;
251 }
252
253 } lws_end_foreach_dll(d);
254
255 /*
256 * Either best_gw is the best gw route and we set *best_gw_priority to
257 * the best one's priority, or we're returning NULL as no network or
258 * gw route for dest.
259 */
260
261 lwsl_cx_info(pt->context, "returning %p", best_gw);
262
263 return best_gw;
264 }
265
266 /*
267 * Determine if the source still exists
268 */
269
270 lws_route_t *
_lws_route_find_source(struct lws_context_per_thread * pt,const lws_sockaddr46 * src)271 _lws_route_find_source(struct lws_context_per_thread *pt,
272 const lws_sockaddr46 *src)
273 {
274 lws_start_foreach_dll(struct lws_dll2 *, d,
275 lws_dll2_get_head(&pt->context->routing_table)) {
276 lws_route_t *rou = lws_container_of(d, lws_route_t, list);
277
278 // _lws_routing_entry_dump(rou);
279
280 if (rou->src.sa4.sin_family &&
281 !lws_sa46_compare_ads(src, &rou->src))
282 /*
283 * Source route still exists
284 */
285 return rou;
286
287 } lws_end_foreach_dll(d);
288
289 return NULL;
290 }
291
292 int
_lws_route_check_wsi(struct lws * wsi)293 _lws_route_check_wsi(struct lws *wsi)
294 {
295 struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
296 char buf[72];
297
298 if (!wsi->sa46_peer.sa4.sin_family ||
299 #if defined(LWS_WITH_UNIX_SOCK)
300 wsi->unix_skt ||
301 wsi->sa46_peer.sa4.sin_family == AF_UNIX ||
302 #endif
303 wsi->desc.sockfd == LWS_SOCK_INVALID)
304 /* not a socket, cannot judge by route, or not connected,
305 * leave it alone */
306 return 0; /* OK */
307
308 /* the route to the peer is still workable? */
309
310 if (!_lws_route_est_outgoing(pt, &wsi->sa46_peer)) {
311 /* no way to talk to the peer */
312 lwsl_wsi_notice(wsi, "dest route gone");
313 return 1;
314 }
315
316 /* the source address is still workable? */
317
318 lws_sa46_write_numeric_address(&wsi->sa46_local,
319 buf, sizeof(buf));
320 //lwsl_notice("%s: %s sa46_local %s fam %d\n", __func__, wsi->lc.gutag,
321 // buf, wsi->sa46_local.sa4.sin_family);
322
323 if (wsi->sa46_local.sa4.sin_family &&
324 !_lws_route_find_source(pt, &wsi->sa46_local)) {
325
326 lws_sa46_write_numeric_address(&wsi->sa46_local,
327 buf, sizeof(buf));
328 lwsl_wsi_notice(wsi, "source %s gone", buf);
329
330 return 1;
331 }
332
333 lwsl_wsi_debug(wsi, "source + dest OK");
334
335 return 0;
336 }
337
338 int
_lws_route_pt_close_unroutable(struct lws_context_per_thread * pt)339 _lws_route_pt_close_unroutable(struct lws_context_per_thread *pt)
340 {
341 struct lws *wsi;
342 unsigned int n;
343
344 if (!pt->context->nl_initial_done
345 #if defined(LWS_WITH_SYS_STATE)
346 ||
347 pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG
348 #endif
349 )
350 return 0;
351
352 lwsl_cx_debug(pt->context, "in");
353 #if defined(_DEBUG)
354 _lws_routing_table_dump(pt->context);
355 #endif
356
357 for (n = 0; n < pt->fds_count; n++) {
358 wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
359 if (!wsi)
360 continue;
361
362 if (_lws_route_check_wsi(wsi)) {
363 lwsl_wsi_info(wsi, "culling wsi");
364 lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
365 }
366 }
367
368 return 0;
369 }
370
371 int
_lws_route_pt_close_route_users(struct lws_context_per_thread * pt,lws_route_uidx_t uidx)372 _lws_route_pt_close_route_users(struct lws_context_per_thread *pt,
373 lws_route_uidx_t uidx)
374 {
375 struct lws *wsi;
376 unsigned int n;
377
378 if (!uidx)
379 return 0;
380
381 lwsl_cx_info(pt->context, "closing users of route %d", uidx);
382
383 for (n = 0; n < pt->fds_count; n++) {
384 wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
385 if (!wsi)
386 continue;
387
388 if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
389 #if defined(LWS_WITH_UNIX_SOCK)
390 !wsi->unix_skt &&
391 wsi->sa46_peer.sa4.sin_family != AF_UNIX &&
392 #endif
393 wsi->sa46_peer.sa4.sin_family &&
394 wsi->peer_route_uidx == uidx) {
395 lwsl_wsi_notice(wsi, "culling wsi");
396 lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
397 }
398 }
399
400 return 0;
401 }
402