/src/libwebsockets/lib/core-net/route.c
Line | Count | Source (jump to first uncovered line) |
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 |
38 | | _lws_routing_entry_dump(struct lws_context *cx, lws_route_t *rou) |
39 | 0 | { |
40 | 0 | char sa[48], fin[192], *end = &fin[sizeof(fin)]; |
41 | 0 | char *it = fin; |
42 | 0 | int n; |
43 | |
|
44 | 0 | fin[0] = '\0'; |
45 | |
|
46 | 0 | if (rou->dest.sa4.sin_family) { |
47 | 0 | lws_sa46_write_numeric_address(&rou->dest, sa, sizeof(sa)); |
48 | 0 | n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), |
49 | 0 | "dst: %s/%d, ", sa, rou->dest_len); |
50 | 0 | it = it + n; |
51 | 0 | } |
52 | |
|
53 | 0 | if (rou->src.sa4.sin_family) { |
54 | 0 | lws_sa46_write_numeric_address(&rou->src, sa, sizeof(sa)); |
55 | 0 | n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), |
56 | 0 | "src: %s/%d, ", sa, rou->src_len); |
57 | 0 | it = it + n; |
58 | 0 | } |
59 | |
|
60 | 0 | if (rou->gateway.sa4.sin_family) { |
61 | 0 | lws_sa46_write_numeric_address(&rou->gateway, sa, sizeof(sa)); |
62 | 0 | n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), |
63 | 0 | "gw: %s, ", sa); |
64 | 0 | it = it + n; |
65 | 0 | } |
66 | |
|
67 | 0 | lwsl_cx_info(cx, " %s ifidx: %d, pri: %d, proto: %d\n", fin, |
68 | 0 | rou->if_idx, rou->priority, rou->proto); |
69 | 0 | } |
70 | | |
71 | | void |
72 | | _lws_routing_table_dump(struct lws_context *cx) |
73 | 0 | { |
74 | 0 | lwsl_cx_info(cx, "\n"); |
75 | 0 | lws_start_foreach_dll(struct lws_dll2 *, d, |
76 | 0 | lws_dll2_get_head(&cx->routing_table)) { |
77 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
78 | |
|
79 | 0 | _lws_routing_entry_dump(cx, rou); |
80 | 0 | } lws_end_foreach_dll(d); |
81 | 0 | } |
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 |
104 | | _lws_route_get_uidx(struct lws_context *cx) |
105 | 0 | { |
106 | 0 | lws_route_uidx_t ou; |
107 | |
|
108 | 0 | if (!cx->route_uidx) |
109 | 0 | cx->route_uidx++; |
110 | |
|
111 | 0 | ou = cx->route_uidx; |
112 | |
|
113 | 0 | do { |
114 | 0 | uint8_t again = 0; |
115 | | |
116 | | /* Anybody in the table already uses the pt's next uidx? */ |
117 | |
|
118 | 0 | lws_start_foreach_dll(struct lws_dll2 *, d, |
119 | 0 | lws_dll2_get_head(&cx->routing_table)) { |
120 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
121 | |
|
122 | 0 | if (rou->uidx == cx->route_uidx) { |
123 | | /* if so, bump and restart the check */ |
124 | 0 | cx->route_uidx++; |
125 | 0 | if (!cx->route_uidx) |
126 | 0 | cx->route_uidx++; |
127 | 0 | if (cx->route_uidx == ou) { |
128 | 0 | assert(0); /* we have filled up the 8-bit uidx space? */ |
129 | 0 | return 0; |
130 | 0 | } |
131 | 0 | again = 1; |
132 | 0 | break; |
133 | 0 | } |
134 | 0 | } lws_end_foreach_dll(d); |
135 | |
|
136 | 0 | if (!again) |
137 | 0 | return cx->route_uidx++; |
138 | 0 | } while (1); |
139 | 0 | } |
140 | | |
141 | | lws_route_t * |
142 | | _lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags) |
143 | 0 | { |
144 | 0 | lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, |
145 | 0 | lws_dll2_get_head(&pt->context->routing_table)) { |
146 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
147 | |
|
148 | 0 | if ((!(flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->src, &rou->src)) && |
149 | 0 | (!(flags & LRR_MATCH_DST) || !lws_sa46_compare_ads(&robj->dest, &rou->dest)) && |
150 | 0 | (!robj->gateway.sa4.sin_family || |
151 | 0 | !lws_sa46_compare_ads(&robj->gateway, &rou->gateway)) && |
152 | 0 | robj->dest_len <= rou->dest_len && |
153 | 0 | robj->if_idx == rou->if_idx && |
154 | 0 | ((flags & LRR_IGNORE_PRI) || |
155 | 0 | robj->priority == rou->priority) |
156 | 0 | ) { |
157 | 0 | lwsl_cx_info(pt->context, "deleting route"); |
158 | 0 | _lws_route_pt_close_route_users(pt, robj->uidx); |
159 | 0 | lws_dll2_remove(&rou->list); |
160 | 0 | lws_free(rou); |
161 | 0 | } |
162 | |
|
163 | 0 | } lws_end_foreach_dll_safe(d, d1); |
164 | |
|
165 | 0 | return NULL; |
166 | 0 | } |
167 | | |
168 | | void |
169 | | _lws_route_table_empty(struct lws_context_per_thread *pt) |
170 | 0 | { |
171 | |
|
172 | 0 | if (!pt->context) |
173 | 0 | return; |
174 | | |
175 | 0 | lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, |
176 | 0 | lws_dll2_get_head(&pt->context->routing_table)) { |
177 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
178 | |
|
179 | 0 | lws_dll2_remove(&rou->list); |
180 | 0 | lws_free(rou); |
181 | |
|
182 | 0 | } lws_end_foreach_dll_safe(d, d1); |
183 | 0 | } |
184 | | |
185 | | void |
186 | | _lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx) |
187 | 0 | { |
188 | 0 | lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, |
189 | 0 | lws_dll2_get_head(&pt->context->routing_table)) { |
190 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
191 | |
|
192 | 0 | if (rou->if_idx == idx) { |
193 | 0 | lws_dll2_remove(&rou->list); |
194 | 0 | lws_free(rou); |
195 | 0 | } |
196 | |
|
197 | 0 | } lws_end_foreach_dll_safe(d, d1); |
198 | 0 | } |
199 | | |
200 | | lws_route_t * |
201 | | _lws_route_est_outgoing(struct lws_context_per_thread *pt, |
202 | | const lws_sockaddr46 *dest) |
203 | 0 | { |
204 | 0 | lws_route_t *best_gw = NULL; |
205 | 0 | int best_gw_priority = INT_MAX; |
206 | |
|
207 | 0 | if (!dest->sa4.sin_family) { |
208 | 0 | lwsl_cx_notice(pt->context, "dest has 0 AF"); |
209 | | /* leave it alone */ |
210 | 0 | return NULL; |
211 | 0 | } |
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 | 0 | lws_start_foreach_dll(struct lws_dll2 *, d, |
221 | 0 | lws_dll2_get_head(&pt->context->routing_table)) { |
222 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
223 | | |
224 | | // _lws_routing_entry_dump(rou); |
225 | |
|
226 | 0 | if (rou->dest.sa4.sin_family && |
227 | 0 | !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 | 0 | return rou; |
234 | | |
235 | 0 | lwsl_cx_debug(pt->context, "dest af %d, rou gw af %d, pri %d", |
236 | 0 | dest->sa4.sin_family, rou->gateway.sa4.sin_family, |
237 | 0 | rou->priority); |
238 | |
|
239 | 0 | 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 | 0 | (dest->sa4.sin_family == rou->gateway.sa4.sin_family || |
250 | 0 | (dest->sa4.sin_family == AF_INET && |
251 | 0 | rou->gateway.sa4.sin_family == AF_INET6)) && |
252 | 0 | rou->priority < best_gw_priority) { |
253 | 0 | lwsl_cx_info(pt->context, "gw hit"); |
254 | 0 | best_gw_priority = rou->priority; |
255 | 0 | best_gw = rou; |
256 | 0 | } |
257 | |
|
258 | 0 | } 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 | 0 | lwsl_cx_info(pt->context, "returning %p", best_gw); |
267 | |
|
268 | 0 | return best_gw; |
269 | 0 | } |
270 | | |
271 | | /* |
272 | | * Determine if the source still exists |
273 | | */ |
274 | | |
275 | | lws_route_t * |
276 | | _lws_route_find_source(struct lws_context_per_thread *pt, |
277 | | const lws_sockaddr46 *src) |
278 | 0 | { |
279 | 0 | lws_start_foreach_dll(struct lws_dll2 *, d, |
280 | 0 | lws_dll2_get_head(&pt->context->routing_table)) { |
281 | 0 | lws_route_t *rou = lws_container_of(d, lws_route_t, list); |
282 | | |
283 | | // _lws_routing_entry_dump(rou); |
284 | |
|
285 | 0 | if (rou->src.sa4.sin_family && |
286 | 0 | !lws_sa46_compare_ads(src, &rou->src)) |
287 | | /* |
288 | | * Source route still exists |
289 | | */ |
290 | 0 | return rou; |
291 | |
|
292 | 0 | } lws_end_foreach_dll(d); |
293 | |
|
294 | 0 | return NULL; |
295 | 0 | } |
296 | | |
297 | | int |
298 | | _lws_route_check_wsi(struct lws *wsi) |
299 | 0 | { |
300 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
301 | 0 | char buf[72]; |
302 | |
|
303 | 0 | if (!wsi->sa46_peer.sa4.sin_family || |
304 | 0 | #if defined(LWS_WITH_UNIX_SOCK) |
305 | 0 | wsi->unix_skt || |
306 | 0 | wsi->sa46_peer.sa4.sin_family == AF_UNIX || |
307 | 0 | #endif |
308 | 0 | wsi->desc.sockfd == LWS_SOCK_INVALID) |
309 | | /* not a socket, cannot judge by route, or not connected, |
310 | | * leave it alone */ |
311 | 0 | return 0; /* OK */ |
312 | | |
313 | | /* the route to the peer is still workable? */ |
314 | | |
315 | 0 | if (!_lws_route_est_outgoing(pt, &wsi->sa46_peer)) { |
316 | | /* no way to talk to the peer */ |
317 | 0 | lwsl_wsi_notice(wsi, "dest route gone"); |
318 | 0 | return 1; |
319 | 0 | } |
320 | | |
321 | | /* the source address is still workable? */ |
322 | | |
323 | 0 | lws_sa46_write_numeric_address(&wsi->sa46_local, |
324 | 0 | 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 | 0 | if (wsi->sa46_local.sa4.sin_family && |
329 | 0 | !_lws_route_find_source(pt, &wsi->sa46_local)) { |
330 | |
|
331 | 0 | lws_sa46_write_numeric_address(&wsi->sa46_local, |
332 | 0 | buf, sizeof(buf)); |
333 | 0 | lwsl_wsi_notice(wsi, "source %s gone", buf); |
334 | |
|
335 | 0 | return 1; |
336 | 0 | } |
337 | | |
338 | 0 | lwsl_wsi_debug(wsi, "source + dest OK"); |
339 | |
|
340 | 0 | return 0; |
341 | 0 | } |
342 | | |
343 | | int |
344 | | _lws_route_pt_close_unroutable(struct lws_context_per_thread *pt) |
345 | 0 | { |
346 | 0 | struct lws *wsi; |
347 | 0 | unsigned int n; |
348 | |
|
349 | 0 | if (!pt->context->nl_initial_done |
350 | 0 | #if defined(LWS_WITH_SYS_STATE) |
351 | 0 | || |
352 | 0 | pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG |
353 | 0 | #endif |
354 | 0 | ) |
355 | 0 | return 0; |
356 | | |
357 | 0 | lwsl_cx_debug(pt->context, "in"); |
358 | 0 | #if defined(_DEBUG) |
359 | 0 | _lws_routing_table_dump(pt->context); |
360 | 0 | #endif |
361 | |
|
362 | 0 | for (n = 0; n < pt->fds_count; n++) { |
363 | 0 | wsi = wsi_from_fd(pt->context, pt->fds[n].fd); |
364 | 0 | if (!wsi) |
365 | 0 | continue; |
366 | | |
367 | 0 | if (_lws_route_check_wsi(wsi)) { |
368 | 0 | lwsl_wsi_info(wsi, "culling wsi"); |
369 | 0 | lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); |
370 | 0 | } |
371 | 0 | } |
372 | |
|
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | int |
377 | | _lws_route_pt_close_route_users(struct lws_context_per_thread *pt, |
378 | | lws_route_uidx_t uidx) |
379 | 0 | { |
380 | 0 | struct lws *wsi; |
381 | 0 | unsigned int n; |
382 | |
|
383 | 0 | if (!uidx) |
384 | 0 | return 0; |
385 | | |
386 | 0 | lwsl_cx_info(pt->context, "closing users of route %d", uidx); |
387 | |
|
388 | 0 | for (n = 0; n < pt->fds_count; n++) { |
389 | 0 | wsi = wsi_from_fd(pt->context, pt->fds[n].fd); |
390 | 0 | if (!wsi) |
391 | 0 | continue; |
392 | | |
393 | 0 | if (wsi->desc.sockfd != LWS_SOCK_INVALID && |
394 | 0 | #if defined(LWS_WITH_UNIX_SOCK) |
395 | 0 | !wsi->unix_skt && |
396 | 0 | wsi->sa46_peer.sa4.sin_family != AF_UNIX && |
397 | 0 | #endif |
398 | 0 | wsi->sa46_peer.sa4.sin_family && |
399 | 0 | wsi->peer_route_uidx == uidx) { |
400 | 0 | lwsl_wsi_notice(wsi, "culling wsi"); |
401 | 0 | lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); |
402 | 0 | } |
403 | 0 | } |
404 | |
|
405 | 0 | return 0; |
406 | 0 | } |