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