Coverage Report

Created: 2026-07-16 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/netlink/ops-netlink.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2021 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
 * We also need to understand the source addresses of possible outgoing routes,
28
 * and follow LINK down (ifconfig down) to clean up routes on the interface idx
29
 * going down that are not otherwise cleaned.
30
 */
31
32
#include <private-lib-core.h>
33
34
#include <asm/types.h>
35
#include <sys/socket.h>
36
#include <linux/netlink.h>
37
#include <linux/rtnetlink.h>
38
39
#ifndef IFA_FLAGS
40
0
#define IFA_FLAGS 8
41
#endif
42
43
//#define lwsl_netlink lwsl_notice
44
0
#define lwsl_cx_netlink   lwsl_cx_info
45
0
#define lwsl_cx_netlink_debug lwsl_cx_debug
46
47
static void
48
lws_netlink_coldplug_done_cb(lws_sorted_usec_list_t *sul)
49
0
{
50
0
  struct lws_context *ctx = lws_container_of(sul, struct lws_context,
51
0
               sul_nl_coldplug);
52
0
  ctx->nl_initial_done = 1;
53
0
#if defined(LWS_WITH_SYS_STATE)
54
  /* if nothing is there to intercept anything, go all the way */
55
0
  lws_state_transition_steps(&ctx->mgr_system, LWS_SYSTATE_OPERATIONAL);
56
0
#endif
57
0
}
58
59
static lws_handling_result_t
60
rops_handle_POLLIN_netlink(struct lws_context_per_thread *pt, struct lws *wsi,
61
         struct lws_pollfd *pollfd)
62
0
{
63
0
  struct lws_context  *cx = pt->context;
64
0
  uint8_t s[4096]
65
0
#if defined(_DEBUG)
66
0
          , route_change = 0
67
0
#endif
68
0
#if defined(LWS_WITH_SYS_SMD)
69
0
    , gateway_change = 0
70
0
#endif
71
0
      ;
72
0
  struct sockaddr_nl  nladdr;
73
0
  lws_route_t   robj, *rou;
74
0
  struct nlmsghdr   *h;
75
0
  struct msghdr   msg;
76
0
  struct iovec    iov;
77
0
  unsigned int    n, removed;
78
0
  char      buf[72];
79
80
0
  if (!(pollfd->revents & LWS_POLLIN))
81
0
    return LWS_HPI_RET_HANDLED;
82
83
0
  memset(&msg, 0, sizeof(msg));
84
85
0
  iov.iov_base    = (void *)s;
86
0
  iov.iov_len   = sizeof(s);
87
88
0
  msg.msg_name    = (void *)&(nladdr);
89
0
  msg.msg_namelen   = sizeof(nladdr);
90
91
0
  msg.msg_iov   = &iov;
92
0
  msg.msg_iovlen    = 1;
93
94
0
  n = (unsigned int)recvmsg(wsi->desc.sockfd, &msg, 0);
95
0
  if ((int)n < 0) {
96
0
    lwsl_cx_notice(cx, "recvmsg failed");
97
0
    return LWS_HPI_RET_PLEASE_CLOSE_ME;
98
0
  }
99
100
  // lwsl_hexdump_notice(s, (size_t)n);
101
102
0
  h = (struct nlmsghdr *)s;
103
104
  /* we can get a bunch of messages coalesced in one read*/
105
106
0
  for ( ; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)) {
107
0
    struct ifaddrmsg *ifam;
108
0
    struct rtattr *ra;
109
0
    struct rtmsg *rm;
110
0
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
111
0
    struct ndmsg *nd;
112
0
#endif
113
0
    unsigned int ra_len;
114
0
    uint8_t *p;
115
116
0
    struct ifinfomsg *ifi;
117
0
    struct rtattr *attribute;
118
0
    unsigned int len;
119
120
0
    lwsl_cx_netlink(cx, "RTM %d", h->nlmsg_type);
121
122
0
    memset(&robj, 0, sizeof(robj));
123
0
    robj.if_idx = -1;
124
0
    robj.priority = -1;
125
0
    rm = (struct rtmsg *)NLMSG_DATA(h);
126
127
    /*
128
     * We have to care about NEWLINK so we can understand when a
129
     * network interface went down, and clear the related routes.
130
     *
131
     * We don't get individual DELROUTEs for these.
132
     */
133
134
0
    switch (h->nlmsg_type) {
135
0
    case RTM_NEWLINK:
136
137
0
      ifi = NLMSG_DATA(h);
138
0
      len = (unsigned int)(h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
139
140
      /* loop over all attributes for the NEWLINK message */
141
0
      for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len);
142
0
           attribute = RTA_NEXT(attribute, len)) {
143
0
        lwsl_cx_netlink(cx, "if attr %d",
144
0
              (int)attribute->rta_type);
145
0
        switch(attribute->rta_type) {
146
0
        case IFLA_IFNAME:
147
0
          lwsl_cx_netlink(cx, "NETLINK ifidx %d : %s",
148
0
                 ifi->ifi_index,
149
0
                 (char *)RTA_DATA(attribute));
150
0
          break;
151
0
        default:
152
0
          break;
153
0
        } /* switch */
154
0
      } /* for loop */
155
156
0
      lwsl_cx_netlink(cx, "NEWLINK ifi_index %d, flags 0x%x",
157
0
          ifi->ifi_index, ifi->ifi_flags);
158
159
      /*
160
       * Despite "New"link this is actually telling us there
161
       * is some change on the network interface IFF_ state
162
       */
163
164
0
      if (!(ifi->ifi_flags & IFF_UP)) {
165
        /*
166
         * Interface is down, so scrub all routes that
167
         * applied to it
168
         */
169
0
        lwsl_cx_netlink(cx, "NEWLINK: ifdown %d",
170
0
            ifi->ifi_index);
171
0
        lws_pt_lock(pt, __func__);
172
0
        _lws_route_table_ifdown(pt, ifi->ifi_index);
173
0
        lws_pt_unlock(pt);
174
0
      }
175
0
      continue; /* ie, not break, no second half */
176
177
0
    case RTM_NEWADDR:
178
0
    case RTM_DELADDR:
179
180
0
      ifam = (struct ifaddrmsg *)NLMSG_DATA(h);
181
182
0
      robj.source_ads = 1;
183
0
      robj.dest_len = ifam->ifa_prefixlen;
184
0
      robj.if_idx = (int)ifam->ifa_index;
185
0
      robj.scope = ifam->ifa_scope;
186
0
      robj.ifa_flags = ifam->ifa_flags;
187
0
      robj.dest.sa4.sin_family = ifam->ifa_family;
188
189
      /* address attributes */
190
0
      ra = (struct rtattr *)IFA_RTA(ifam);
191
0
      ra_len = (unsigned int)IFA_PAYLOAD(h);
192
193
0
      lwsl_cx_netlink(cx, "%s",
194
0
        h->nlmsg_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR");
195
196
      // Parse attributes.
197
0
      for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
198
        //lwsl_cx_netlink_debug(cx, "%s: IFA %d\n", __func__, ra->rta_type);
199
0
        switch (ra->rta_type) {
200
0
        case IFA_LOCAL:
201
          // Local address
202
0
          lws_sa46_copy_address(&robj.src, RTA_DATA(ra), rm->rtm_family);
203
0
          robj.src_len = rm->rtm_src_len;
204
0
          lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
205
0
          lwsl_cx_netlink_debug(cx, "IFA_LOCAL: %s/%d", buf, robj.src_len);
206
0
          break;
207
0
        case IFA_ADDRESS:
208
          // Prefix address, not local interface.
209
0
          lws_sa46_copy_address(&robj.dest, RTA_DATA(ra), rm->rtm_family);
210
0
          robj.dest_len = rm->rtm_dst_len;
211
0
          lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
212
0
          lwsl_cx_netlink_debug(cx, "IFA_ADDRESS: %s/%d", buf, robj.dest_len);
213
0
          break;
214
0
        case IFA_FLAGS:
215
0
          lwsl_cx_netlink_debug(cx, "IFA_FLAGS: 0x%x (not handled)",
216
0
              *(unsigned int*)RTA_DATA(ra));
217
0
          break;
218
0
        case IFA_BROADCAST:
219
0
          lwsl_cx_netlink_debug(cx, "IFA_BROADCAST (not handled)");
220
0
          break;
221
0
        case IFA_ANYCAST:
222
0
          lwsl_cx_netlink_debug(cx, "IFA_ANYCAST (not handled)");
223
0
          break;
224
0
        case IFA_CACHEINFO:
225
0
          lwsl_cx_netlink_debug(cx, "IFA_CACHEINFO (not handled)");
226
0
          break;
227
0
        case IFA_LABEL:
228
0
          strncpy(buf, RTA_DATA(ra), sizeof(buf));
229
0
          buf[sizeof(buf)-1] = '\0';
230
0
          lwsl_cx_netlink_debug(cx, "IFA_LABEL: %s (not used)", buf);
231
0
          break;
232
0
        default:
233
0
          lwsl_cx_netlink_debug(cx, "unknown IFA attr type %d", ra->rta_type);
234
0
          break;
235
0
        }
236
        //lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
237
0
      } /* for */
238
239
      /*
240
       * almost nothing interesting within IFA_* attributes:
241
       * so skip it and goto to the second half
242
       */
243
0
      goto second_half;
244
245
0
    case RTM_NEWROUTE:
246
0
    case RTM_DELROUTE:
247
248
0
      lwsl_cx_netlink(cx, "%s",
249
0
             h->nlmsg_type == RTM_NEWROUTE ?
250
0
                 "NEWROUTE" : "DELROUTE");
251
252
      /* route attributes */
253
0
      ra = (struct rtattr *)RTM_RTA(rm);
254
0
      ra_len = (unsigned int)RTM_PAYLOAD(h);
255
0
      break;
256
257
0
    case RTM_DELNEIGH:
258
0
    case RTM_NEWNEIGH:
259
0
      lwsl_cx_netlink(cx, "%s", h->nlmsg_type ==
260
0
            RTM_NEWNEIGH ? "NEWNEIGH" :
261
0
                     "DELNEIGH");
262
0
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
263
0
      nd = (struct ndmsg *)rm;
264
0
      lwsl_cx_netlink(cx, "fam %u, ifidx %u, flags 0x%x",
265
0
            nd->ndm_family, nd->ndm_ifindex,
266
0
            nd->ndm_flags);
267
0
#endif
268
0
      ra = (struct rtattr *)RTM_RTA(rm);
269
0
      ra_len = (unsigned int)RTM_PAYLOAD(h);
270
0
      for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
271
0
        lwsl_cx_netlink(cx, "atr %d", ra->rta_type);
272
0
        switch (ra->rta_type) {
273
0
        case NDA_DST:
274
0
          lwsl_cx_netlink(cx, "dst len %d",
275
0
              ra->rta_len);
276
0
          break;
277
0
        }
278
0
      }
279
0
      lws_pt_lock(pt, __func__);
280
0
      _lws_route_pt_close_unroutable(pt);
281
0
      lws_pt_unlock(pt);
282
0
      continue;
283
284
0
    default:
285
0
      lwsl_cx_netlink(cx, "*** Unknown RTM_%d",
286
0
          h->nlmsg_type);
287
0
      continue;
288
0
    } /* switch */
289
290
0
    robj.proto = rm->rtm_protocol;
291
292
    // iterate over route attributes
293
0
    for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
294
      // lwsl_netlink("%s: atr %d\n", __func__, ra->rta_type);
295
0
      switch (ra->rta_type) {
296
0
      case RTA_PREFSRC: /* protocol ads: preferred src ads */
297
0
      case RTA_SRC:
298
0
        lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
299
0
              rm->rtm_family);
300
0
        robj.src_len = rm->rtm_src_len;
301
0
        lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
302
0
        if (ra->rta_type == RTA_SRC)
303
0
          lwsl_cx_netlink_debug(cx, "RTA_SRC: %s/%d", buf, robj.src_len);
304
0
        else
305
0
          lwsl_cx_netlink_debug(cx, "RTA_PREFSRC: %s/%d", buf, robj.src_len);
306
0
        break;
307
0
      case RTA_DST:
308
        /* check if is local addr -> considering it as src addr too */
309
0
        if (rm->rtm_type == RTN_LOCAL &&
310
0
            ((rm->rtm_family == AF_INET && rm->rtm_dst_len == 32) ||
311
0
             (rm->rtm_family == AF_INET6 && rm->rtm_dst_len == 128))) {
312
0
          lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
313
0
              rm->rtm_family);
314
0
          lwsl_cx_netlink_debug(cx, "Local addr: RTA_DST -> added to RTA_SRC");
315
0
        }
316
317
0
        lws_sa46_copy_address(&robj.dest, RTA_DATA(ra),
318
0
                  rm->rtm_family);
319
0
        robj.dest_len = rm->rtm_dst_len;
320
0
        lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
321
0
        lwsl_cx_netlink_debug(cx, "RTA_DST: %s/%d", buf, robj.dest_len);
322
0
        break;
323
0
      case RTA_GATEWAY:
324
0
        lws_sa46_copy_address(&robj.gateway, RTA_DATA(ra),
325
0
                  rm->rtm_family);
326
327
0
        lws_sa46_write_numeric_address(&robj.gateway, buf, sizeof(buf));
328
0
        lwsl_cx_netlink_debug(cx, "RTA_GATEWAY: %s", buf);
329
0
#if defined(LWS_WITH_SYS_SMD)
330
0
        gateway_change = 1;
331
0
#endif
332
0
        break;
333
0
      case RTA_IIF: /* int: input interface index */
334
0
      case RTA_OIF: /* int: output interface index */
335
0
        robj.if_idx = *(int *)RTA_DATA(ra);
336
0
        lwsl_cx_netlink_debug(cx, "RTA_IIF/RTA_OIF: %d", robj.if_idx);
337
0
        break;
338
0
      case RTA_PRIORITY: /* int: priority of route */
339
0
        p = RTA_DATA(ra);
340
0
        robj.priority = p[3] << 24 | p[2] << 16 |
341
0
             p[1] << 8  | p[0];
342
0
        lwsl_cx_netlink_debug(cx, "RTA_PRIORITY: %d", robj.priority);
343
0
        break;
344
0
      case RTA_CACHEINFO: /* struct rta_cacheinfo */
345
0
        lwsl_cx_netlink_debug(cx, "RTA_CACHEINFO (not handled)");
346
0
        break;
347
0
#if defined(LWS_HAVE_RTA_PREF)
348
0
      case RTA_PREF: /* char: RFC4191 v6 router preference */
349
0
        lwsl_cx_netlink_debug(cx, "RTA_PREF (not handled)");
350
0
        break;
351
0
#endif
352
0
      case RTA_TABLE: /* int */
353
0
        lwsl_cx_netlink_debug(cx, "RTA_TABLE (not handled)");
354
0
        break;
355
356
0
      default:
357
0
        lwsl_cx_netlink_debug(cx, "unknown attr type %d", ra->rta_type);
358
0
        break;
359
0
      }
360
      //lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
361
0
    } /* for */
362
363
    /*
364
     * the second half, once all the attributes were collected
365
     */
366
0
second_half:
367
0
    switch (h->nlmsg_type) {
368
369
0
    case RTM_DELROUTE:
370
      /*
371
       * This will also take down wsi marked as using it
372
       */
373
0
      lwsl_cx_netlink(cx, "DELROUTE: if_idx %d",
374
0
          robj.if_idx);
375
0
      lws_pt_lock(pt, __func__);
376
0
      _lws_route_remove(pt, &robj, 0);
377
0
      lws_pt_unlock(pt);
378
0
      goto inform_l;
379
380
0
    case RTM_NEWROUTE:
381
382
      /*
383
       * We don't want any routing debris like /32 or broadcast
384
       * in our routing table... we will collect source addresses
385
       * bound to interfaces via NEWADDR
386
       */
387
0
      if (rm->rtm_type != RTN_UNICAST
388
0
          && rm->rtm_type != RTN_LOCAL) {
389
0
        lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (%s)",
390
0
            rm->rtm_type == RTN_BROADCAST   ? "broadcast" :
391
0
            rm->rtm_type == RTN_ANYCAST     ? "anycast" :
392
0
            rm->rtm_type == RTN_MULTICAST   ? "multicast" :
393
0
            rm->rtm_type == RTN_UNREACHABLE   ? "unreachable" :
394
0
            rm->rtm_type == RTN_NAT   ? "nat" :
395
0
            rm->rtm_type == RTN_UNSPEC      ? "unspecified" :
396
0
                    "other");
397
0
        break;
398
0
      }
399
400
0
      if (rm->rtm_flags & RTM_F_CLONED) {
401
0
        lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (cloned)");
402
0
        break;
403
0
      }
404
405
0
      lwsl_cx_netlink(cx, "NEWROUTE: ACCEPTED (if_idx %d)",
406
0
          robj.if_idx);
407
408
0
#if defined(_DEBUG)
409
0
      _lws_routing_entry_dump(cx, &robj);
410
0
#endif
411
412
      /*
413
       * 1. Allocate new route for linked-list.
414
       *    (robj is on stack, do NOT use)
415
       */    
416
0
      rou = lws_malloc(sizeof(*rou), __func__);
417
0
      if (!rou) {
418
0
        lwsl_cx_err(cx, "oom");
419
0
        return LWS_HPI_RET_HANDLED;
420
0
      }
421
0
      *rou = robj;
422
423
      // 2. Remove duplicates and add route (both under a lock).
424
0
      lws_pt_lock(pt, __func__);
425
426
      /*
427
       * Is robj a dupe in the routing table already?
428
       *
429
       * match on pri ignore == set pri and skip
430
       * no match == add
431
       *
432
       * returns zero ALWAYS
433
       *
434
       * We could be adding a route to the same destination with
435
       * a higher or lower priority from a different source, so why
436
       * all existing routes? Only remove if its the same source and
437
       * destination, effectively a change in priority.
438
       */
439
0
      _lws_route_remove(pt, &robj,
440
0
          LRR_MATCH_DST | LRR_MATCH_SRC | LRR_IGNORE_PRI);
441
442
      /* add route to linked-list */
443
0
      rou->uidx = _lws_route_get_uidx(cx);
444
0
      lws_dll2_add_tail(&rou->list, &cx->routing_table);
445
0
      lws_pt_unlock(pt);
446
447
0
      lwsl_cx_netlink_debug(cx, "route list size %u",
448
0
        cx->routing_table.count);
449
450
      /*
451
       * 3. Close anyything we cant reach anymore due to the removal.
452
       *    (don't need to or want to do this under lock)
453
       */
454
0
      _lws_route_pt_close_unroutable(pt);
455
456
0
inform_l:
457
0
#if defined(_DEBUG)
458
0
      route_change = 1;
459
0
#endif
460
0
#if defined(LWS_WITH_SYS_SMD)
461
      /*
462
       * Reflect the route add / del event using SMD.
463
       * Participants interested can refer to the pt
464
       * routing table
465
       */
466
0
      (void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
467
0
           "{\"rt\":\"%s\"}\n",
468
0
           (h->nlmsg_type == RTM_NEWROUTE) ?
469
0
            "add" : "del");
470
0
#endif
471
472
0
      break;
473
474
0
    case RTM_DELADDR:
475
0
      lwsl_cx_notice(cx, "DELADDR");
476
0
#if defined(_DEBUG)
477
0
      _lws_routing_entry_dump(cx, &robj);
478
0
#endif
479
0
      lws_pt_lock(pt, __func__);
480
0
      removed = cx->routing_table.count;
481
0
      _lws_route_remove(pt, &robj, LRR_MATCH_SRC | LRR_IGNORE_PRI);
482
0
      removed -= cx->routing_table.count;
483
0
      lws_pt_unlock(pt);
484
0
      _lws_route_pt_close_unroutable(pt);
485
0
      if (removed > 0)
486
0
        goto inform_l;
487
0
      break;
488
489
0
    case RTM_NEWADDR:
490
0
      lwsl_cx_netlink(cx, "NEWADDR (nothing to do)");
491
0
#if defined(_DEBUG)
492
0
      _lws_routing_entry_dump(cx, &robj);
493
0
#endif
494
      /*
495
       * An address alone does not provide new routes.
496
       * NEWADDR will happen when the DHCP lease is being
497
       * renewed, and will likely not change any routes.
498
       */
499
0
      break;
500
501
0
    default:
502
      // lwsl_info("%s: unknown msg type %d\n", __func__,
503
      //    h->nlmsg_type);
504
0
      break;
505
0
    }
506
0
  } /* message iterator */
507
508
0
#if defined(LWS_WITH_SYS_SMD)
509
0
  if (gateway_change)
510
    /*
511
     * If a route with a gw was added or deleted, retrigger captive
512
     * portal detection if we have that
513
     */
514
0
    (void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
515
0
           "{\"trigger\": \"cpdcheck\", "
516
0
           "\"src\":\"gw-change\"}");
517
0
#endif
518
519
0
#if defined(_DEBUG)
520
0
  if (route_change) {
521
0
    lws_context_lock(cx, __func__);
522
0
    _lws_routing_table_dump(cx);
523
0
    lws_context_unlock(cx);
524
0
  }
525
0
#endif
526
527
0
  if (!cx->nl_initial_done &&
528
0
      pt == &cx->pt[0] &&
529
0
      cx->routing_table.count) {
530
    /*
531
     * While netlink info still coming, keep moving the timer for
532
     * calling it "done" to +100ms until after it stops coming
533
     */
534
0
    lws_context_lock(cx, __func__);
535
0
    lws_sul_schedule(cx, 0, &cx->sul_nl_coldplug,
536
0
         lws_netlink_coldplug_done_cb,
537
0
         100 * LWS_US_PER_MS);
538
0
    lws_context_unlock(cx);
539
0
  }
540
541
0
  return LWS_HPI_RET_HANDLED;
542
0
}
543
544
struct nl_req_s {
545
  struct nlmsghdr hdr;
546
  struct rtmsg gen;
547
};
548
549
int
550
rops_pt_init_destroy_netlink(struct lws_context *context,
551
           const struct lws_context_creation_info *info,
552
           struct lws_context_per_thread *pt, int destroy)
553
0
{
554
0
  struct sockaddr_nl sanl;
555
0
  struct nl_req_s req;
556
0
  struct msghdr msg;
557
0
  struct iovec iov;
558
0
  struct lws *wsi;
559
0
  int n, ret = 1;
560
561
0
  if (destroy) {
562
563
    /*
564
     * pt netlink wsi closed + freed as part of pt's destroy
565
     * wsi mass close, just need to take down the routing table
566
     */
567
0
    _lws_route_table_empty(pt);
568
569
0
    return 0;
570
0
  }
571
572
0
  if (context->netlink)
573
0
    return 0;
574
575
0
  if (pt > &context->pt[0])
576
    /* we can only have one netlink socket */
577
0
    return 0;
578
579
0
  lwsl_cx_info(context, "creating netlink skt");
580
581
  /*
582
   * We want a netlink socket per pt as well
583
   */
584
585
0
  lws_context_lock(context, __func__);
586
0
  wsi = __lws_wsi_create_with_role(context, (int)(pt - &context->pt[0]),
587
0
               &role_ops_netlink, NULL);
588
0
  lws_context_unlock(context);
589
0
  if (!wsi)
590
0
    goto bail;
591
592
0
  wsi->desc.sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
593
0
  if (wsi->desc.sockfd == LWS_SOCK_INVALID) {
594
0
    lwsl_cx_err(context, "unable to open netlink");
595
0
    goto bail1;
596
0
  }
597
598
0
  lws_plat_set_nonblocking(wsi->desc.sockfd);
599
600
0
  __lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &wsi->lc,
601
0
      "netlink");
602
603
0
  memset(&sanl, 0, sizeof(sanl));
604
0
  sanl.nl_family    = AF_NETLINK;
605
0
  sanl.nl_groups    = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR
606
0
#if defined(LWS_WITH_IPV6)
607
0
          | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR
608
0
#endif
609
0
         ;
610
611
0
  if (lws_fi(&context->fic, "netlink_bind") ||
612
0
      bind(wsi->desc.sockfd, (struct sockaddr*)&sanl, sizeof(sanl)) < 0) {
613
0
    lwsl_cx_warn(context, "netlink bind failed");
614
0
    ret = 0; /* some systems deny access, just ignore */
615
0
    goto bail2;
616
0
  }
617
618
0
  context->netlink = wsi;
619
0
  if (lws_wsi_inject_to_loop(pt, wsi))
620
0
    goto bail2;
621
622
/*  if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
623
    lwsl_err("%s: pollfd in fail\n", __func__);
624
    goto bail2;
625
  }
626
*/
627
  /*
628
   * Since we're starting the PT, ask to be sent all the existing routes.
629
   *
630
   * This requires CAP_ADMIN, or root... we do this early before dropping
631
   * privs
632
   */
633
634
0
  memset(&sanl, 0, sizeof(sanl));
635
0
  memset(&msg, 0, sizeof(msg));
636
0
  memset(&req, 0, sizeof(req));
637
638
0
  sanl.nl_family    = AF_NETLINK;
639
640
0
  req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.gen));
641
0
  req.hdr.nlmsg_type  = RTM_GETROUTE;
642
0
  req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
643
0
  req.hdr.nlmsg_seq = 1;
644
0
  req.hdr.nlmsg_pid = (uint32_t)getpid();
645
0
  req.gen.rtm_family  = AF_PACKET;
646
0
  req.gen.rtm_table = RT_TABLE_DEFAULT;
647
648
0
  iov.iov_base    = &req;
649
0
  iov.iov_len   = req.hdr.nlmsg_len;
650
0
  msg.msg_iov   = &iov;
651
0
  msg.msg_iovlen    = 1;
652
0
  msg.msg_name    = &sanl;
653
0
  msg.msg_namelen   = sizeof(sanl);
654
655
0
  n = (int)sendmsg(wsi->desc.sockfd, (struct msghdr *)&msg, 0);
656
0
  if (n < 0) {
657
0
    lwsl_cx_notice(context, "rt dump req failed... permissions? errno %d",
658
0
        LWS_ERRNO);
659
0
  }
660
661
  /*
662
   * Responses are going to come asynchronously, let's block moving
663
   * off state IFACE_COLDPLUG until we have had them.  This is important
664
   * since if we don't hold there, when we do get the responses we may
665
   * cull any ongoing connections as unroutable otherwise
666
   */
667
668
0
  lwsl_cx_debug(context, "starting netlink coldplug wait");
669
670
0
  return 0;
671
672
0
bail2:
673
0
  __lws_lc_untag(wsi->a.context, &wsi->lc);
674
0
  compatible_close(wsi->desc.sockfd);
675
0
bail1:
676
0
  lws_dll2_remove(&wsi->pre_natal);
677
0
  lws_free(wsi);
678
0
bail:
679
0
  return ret;
680
0
}
681
682
static const lws_rops_t rops_table_netlink[] = {
683
  /*  1 */ { .pt_init_destroy = rops_pt_init_destroy_netlink },
684
  /*  2 */ { .handle_POLLIN = rops_handle_POLLIN_netlink },
685
};
686
687
const struct lws_role_ops role_ops_netlink = {
688
  /* role name */     "netlink",
689
  /* alpn id */     NULL,
690
691
  /* rops_table */    rops_table_netlink,
692
  /* rops_idx */      {
693
    /* LWS_ROPS_check_upgrades */
694
    /* LWS_ROPS_pt_init_destroy */    0x01,
695
    /* LWS_ROPS_init_vhost */
696
    /* LWS_ROPS_destroy_vhost */      0x00,
697
    /* LWS_ROPS_service_flag_pending */
698
    /* LWS_ROPS_handle_POLLIN */      0x02,
699
    /* LWS_ROPS_handle_POLLOUT */
700
    /* LWS_ROPS_perform_user_POLLOUT */   0x00,
701
    /* LWS_ROPS_callback_on_writable */
702
    /* LWS_ROPS_tx_credit */      0x00,
703
    /* LWS_ROPS_write_role_protocol */
704
    /* LWS_ROPS_encapsulation_parent */   0x00,
705
    /* LWS_ROPS_alpn_negotiated */
706
    /* LWS_ROPS_close_via_role_protocol */  0x00,
707
    /* LWS_ROPS_close_role */
708
    /* LWS_ROPS_close_kill_connection */    0x00,
709
    /* LWS_ROPS_destroy_role */
710
    /* LWS_ROPS_adoption_bind */      0x00,
711
    /* LWS_ROPS_client_bind */
712
    /* LWS_ROPS_issue_keepalive */    0x00,
713
          },
714
715
  /* adoption_cb clnt, srv */ { 0, 0 },
716
  /* rx_cb clnt, srv */   { 0, 0 },
717
  /* writeable cb clnt, srv */  { 0, 0 },
718
  /* close cb clnt, srv */  { 0, 0 },
719
  /* protocol_bind_cb c,s */  { 0, 0 },
720
  /* protocol_unbind_cb c,s */  { 0, 0 },
721
  /* file_handle */   0,
722
};