Coverage Report

Created: 2025-08-26 06:34

/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
}