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