Coverage Report

Created: 2026-02-14 07:00

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