Coverage Report

Created: 2025-10-10 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wpantund/src/util/netif-mgmt.c
Line
Count
Source
1
/*
2
 *
3
 * Copyright (c) 2016 Nest Labs, Inc.
4
 * All rights reserved.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 *
18
 *    Description:
19
 *    This file implements the code which managed the TUN interface.
20
 *
21
 */
22
23
#if HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
27
#ifndef ASSERT_MACROS_USE_SYSLOG
28
#define ASSERT_MACROS_USE_SYSLOG 1
29
#endif
30
31
#include "assert-macros.h"
32
#include "pt.h"
33
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include "netif-mgmt.h"
37
#include <syslog.h>
38
#include <fcntl.h>
39
#include <errno.h>
40
#include <string.h>
41
#include <netinet/in.h>
42
#include <net/if.h>
43
#include <sys/ioctl.h>
44
#include <unistd.h>
45
46
#ifndef __APPLE__
47
#include <linux/if_tun.h>
48
#endif
49
50
#include <net/if.h>
51
#include <netinet/in.h>
52
#include <fcntl.h>
53
#include <sys/stat.h>
54
#include <net/route.h> // AF_ROUTE things
55
56
#ifdef __APPLE__
57
#include <netinet6/in6_var.h>
58
#include <netinet6/nd6.h>   // ND6_INFINITE_LIFETIME
59
#include <net/if_dl.h>      // struct sockaddr_dl
60
#include <net/if_utun.h>
61
#include <net/if.h>
62
#include <netinet/in.h>
63
#include <string.h>
64
#include <sys/socket.h>
65
#include <sys/kern_control.h>
66
#include <sys/ioctl.h>
67
#include <sys/sys_domain.h>
68
#include <sys/kern_event.h>
69
#define IFEF_NOAUTOIPV6LL   0x2000  /* Interface IPv6 LinkLocal address not provided by kernel */
70
#endif
71
72
#ifdef MISSING_STRLCPY
73
#include "missing/strlcpy/strlcpy.h"
74
#endif
75
76
#ifndef SIOCSIFLLADDR
77
#define SIOCSIFLLADDR SIOCSIFHWADDR
78
#endif
79
80
int
81
netif_mgmt_open(void)
82
0
{
83
0
  return socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
84
0
}
85
86
void
87
netif_mgmt_close(int fd)
88
11.3k
{
89
11.3k
  close(fd);
90
11.3k
}
91
92
int netif_mgmt_get_flags(int fd, const char* if_name)
93
32.5k
{
94
32.5k
  int ret = 0;
95
32.5k
  int status = 0;
96
32.5k
  struct ifreq ifr = { };
97
98
32.5k
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
99
100
32.5k
  status = ioctl(fd, SIOCGIFFLAGS, &ifr);
101
102
32.5k
  require_string(status == 0, bail, strerror(errno));
103
104
0
  ret = ifr.ifr_flags;
105
106
32.5k
bail:
107
32.5k
  return ret;
108
0
}
109
110
int netif_mgmt_set_flags(int fd, const char* if_name, int flags)
111
1.87k
{
112
1.87k
  int ret = -1;
113
1.87k
  struct ifreq ifr = { };
114
115
1.87k
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
116
117
1.87k
  ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
118
1.87k
  require_string(ret == 0, bail, strerror(errno));
119
120
0
  ifr.ifr_flags |= flags;
121
122
0
  ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
123
0
  require_string(ret == 0, bail, strerror(errno));
124
125
1.87k
bail:
126
1.87k
  return ret;
127
0
}
128
129
int netif_mgmt_clear_flags(int fd, const char* if_name, int flags)
130
0
{
131
0
  int ret = -1;
132
0
  struct ifreq ifr = { };
133
134
0
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
135
136
0
  ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
137
0
  require_string(ret == 0, bail, strerror(errno));
138
139
0
  ifr.ifr_flags &= ~flags;
140
141
0
  ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
142
0
  require_string(ret == 0, bail, strerror(errno));
143
144
0
bail:
145
0
  return ret;
146
0
}
147
148
bool
149
netif_mgmt_is_up(int fd, const char* if_name)
150
0
{
151
0
  return ((netif_mgmt_get_flags(fd, if_name) & IFF_UP) == IFF_UP);
152
0
}
153
154
bool
155
netif_mgmt_is_running(int fd, const char* if_name)
156
6.18k
{
157
6.18k
  return ((netif_mgmt_get_flags(fd, if_name) & IFF_RUNNING) == IFF_RUNNING);
158
6.18k
}
159
160
int
161
netif_mgmt_set_up(int fd, const char* if_name, bool value)
162
0
{
163
0
  if (value) {
164
0
    return netif_mgmt_set_flags(fd, if_name, IFF_UP);
165
0
  }
166
167
0
  return netif_mgmt_clear_flags(fd, if_name, IFF_UP|IFF_RUNNING);
168
0
}
169
170
int
171
netif_mgmt_set_running(int fd, const char* if_name, bool value)
172
1.87k
{
173
1.87k
  if (value) {
174
1.87k
    return netif_mgmt_set_flags(fd, if_name, IFF_UP|IFF_RUNNING);
175
1.87k
  }
176
177
0
  return netif_mgmt_clear_flags(fd, if_name, IFF_RUNNING);
178
1.87k
}
179
180
int
181
netif_mgmt_set_mtu(int fd, const char* if_name, uint16_t mtu)
182
0
{
183
0
  int ret = -1;
184
0
  struct ifreq ifr = { };
185
186
0
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
187
188
0
  ifr.ifr_mtu = mtu;
189
190
0
  ret = ioctl(fd, SIOCSIFMTU, &ifr);
191
192
0
  require_string(ret == 0, bail, strerror(errno));
193
194
0
bail:
195
0
  return ret;
196
0
}
197
198
int
199
17.4k
netif_mgmt_get_ifindex(int reqfd, const char* if_name) {
200
17.4k
  int ret = -1;
201
202
17.4k
#ifdef SIOGIFINDEX
203
17.4k
  struct ifreq ifr;
204
205
17.4k
  memset(&ifr, 0, sizeof(struct ifreq));
206
17.4k
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
207
208
17.4k
  ret = ioctl(reqfd, SIOGIFINDEX, &ifr);
209
210
17.4k
  if (ret >= 0) {
211
0
    ret = ifr.ifr_ifindex;
212
0
  }
213
17.4k
#endif
214
215
17.4k
  return ret;
216
17.4k
}
217
218
219
220
static inline void
221
apply_mask(
222
  struct in6_addr *address, uint8_t mask
223
  )
224
0
{
225
0
  // Coverty might complain in this function, but don't believe it.
226
0
  // This code has been reviewed carefully and should not misbehave.
227
0
228
0
  if (mask > 128) {
229
0
    mask = 128;
230
0
  }
231
0
232
0
  memset(
233
0
    (void*)(address->s6_addr + ((mask + 7) / 8)),
234
0
    0,
235
0
    16 - ((mask + 7) / 8)
236
0
  );
237
0
238
0
  if (mask % 8) {
239
0
    address->s6_addr[mask / 8] &= ~(0xFF >> (mask % 8));
240
0
  }
241
0
}
242
243
int
244
netif_mgmt_add_ipv6_address(int reqfd, const char* if_name, const uint8_t addr[16], int prefixlen)
245
0
{
246
0
  int ret = -1;
247
248
#ifdef __APPLE__
249
250
  /************* Add address *************/
251
252
  struct in6_aliasreq addreq6 = { };
253
254
  strlcpy(addreq6.ifra_name, if_name, sizeof(addreq6.ifra_name));
255
256
  addreq6.ifra_addr.sin6_family = AF_INET6;
257
  addreq6.ifra_addr.sin6_len = sizeof(addreq6.ifra_addr);
258
  memcpy((void*)&addreq6.ifra_addr.sin6_addr, addr, 16);
259
260
  addreq6.ifra_prefixmask.sin6_family = AF_INET6;
261
  addreq6.ifra_prefixmask.sin6_len = sizeof(addreq6.ifra_prefixmask);
262
  memset((void*)&addreq6.ifra_prefixmask.sin6_addr, 0xFF, 16);
263
  apply_mask(&addreq6.ifra_prefixmask.sin6_addr, prefixlen);
264
265
  addreq6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
266
  addreq6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
267
  addreq6.ifra_lifetime.ia6t_expire = ND6_INFINITE_LIFETIME;
268
  addreq6.ifra_lifetime.ia6t_preferred = ND6_INFINITE_LIFETIME;
269
270
  addreq6.ifra_flags |= IN6_IFF_NODAD;
271
272
  ret = ioctl(reqfd, SIOCAIFADDR_IN6, &addreq6);
273
274
  require_string(ret == 0 || errno == EALREADY, bail, strerror(errno));
275
276
#else
277
278
  /* Linux */
279
280
  // In linux, we need to remove the address first.
281
0
  netif_mgmt_remove_ipv6_address(reqfd, if_name, addr);
282
283
0
#define ifreq_offsetof(x)  offsetof(struct ifreq, x)
284
285
0
  struct in6_ifreq {
286
0
    struct in6_addr ifr6_addr;
287
0
    __u32 ifr6_prefixlen;
288
0
    unsigned int ifr6_ifindex;
289
0
  };
290
291
0
  struct sockaddr_in6 sai;
292
0
  int sockfd;
293
0
  struct in6_ifreq ifr6;
294
295
0
  memset(&sai, 0, sizeof(struct sockaddr));
296
0
  sai.sin6_family = AF_INET6;
297
0
  sai.sin6_port = 0;
298
299
0
  memcpy((void*)&sai.sin6_addr, addr, 16);
300
301
0
  memcpy((char*)&ifr6.ifr6_addr, (char*)&sai.sin6_addr,
302
0
       sizeof(struct in6_addr));
303
304
0
  ret = netif_mgmt_get_ifindex(reqfd, if_name);
305
306
0
  require_string(ret >= 0, bail, strerror(errno));
307
308
0
  ifr6.ifr6_ifindex = ret;
309
0
  ifr6.ifr6_prefixlen = 64;
310
311
0
  ret = ioctl(reqfd, SIOCSIFADDR, &ifr6);
312
313
0
  require_string(ret == 0 || errno == EALREADY, bail, strerror(errno));
314
315
0
#endif
316
0
  ret = 0;
317
0
bail:
318
319
0
  return ret;
320
0
}
321
322
int
323
netif_mgmt_remove_ipv6_address(int reqfd, const char* if_name, const uint8_t addr[16])
324
9.02k
{
325
9.02k
  int ret = -1;
326
327
9.02k
  struct sockaddr_in6 sai = { };
328
329
9.02k
  memset(&sai, 0, sizeof(struct sockaddr));
330
9.02k
  sai.sin6_family = AF_INET6;
331
9.02k
  sai.sin6_port = 0;
332
9.02k
  memcpy((void*)&sai.sin6_addr, addr, 16);
333
334
  /************* Remove address *************/
335
336
#ifdef __APPLE__
337
  sai.sin6_len = sizeof(sai);
338
339
  struct in6_ifreq ifreq6 = { };
340
  strlcpy(ifreq6.ifr_name, if_name, sizeof(ifreq6.ifr_name));
341
342
  ifreq6.ifr_addr = sai;
343
344
  ret = ioctl(reqfd, SIOCDIFADDR_IN6, &ifreq6);
345
#else
346
9.02k
  struct in6_ifreq {
347
9.02k
    struct in6_addr ifr6_addr;
348
9.02k
    __u32 ifr6_prefixlen;
349
9.02k
    unsigned int ifr6_ifindex;
350
9.02k
  };
351
352
9.02k
  struct in6_ifreq ifr6;
353
354
9.02k
  ifr6.ifr6_addr = sai.sin6_addr;
355
9.02k
  ifr6.ifr6_prefixlen = 64;
356
357
9.02k
  ret = netif_mgmt_get_ifindex(reqfd, if_name);
358
359
9.02k
  require_string(ret >= 0, bail, strerror(errno));
360
361
0
  ifr6.ifr6_ifindex = ret;
362
363
0
  ret = ioctl(reqfd, SIOCDIFADDR, &ifr6);
364
0
#endif
365
366
9.02k
bail:
367
9.02k
  return ret;
368
0
}
369
370
int
371
netif_mgmt_add_ipv6_route(int reqfd, const char* if_name, const uint8_t route[16], int prefixlen, uint32_t metric)
372
10.3k
{
373
10.3k
  int ret = -1;
374
375
376
#ifdef __APPLE__
377
378
  /************* Add ROUTE TODO *************/
379
380
#else
381
  /* Linux */
382
383
10.3k
  struct ifreq ifr;
384
10.3k
  struct in6_rtmsg rt;
385
386
10.3k
  memset(&ifr, 0, sizeof(struct ifreq));
387
388
10.3k
  strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
389
390
10.3k
  memset(&rt, 0, sizeof(struct in6_rtmsg));
391
10.3k
  memcpy(rt.rtmsg_dst.s6_addr, route, sizeof(struct in6_addr));
392
10.3k
  rt.rtmsg_dst_len = prefixlen;
393
10.3k
  rt.rtmsg_flags = RTF_UP;
394
10.3k
  if (prefixlen == 128) {
395
161
    rt.rtmsg_flags |= RTF_HOST;
396
161
  }
397
10.3k
  rt.rtmsg_metric = metric;
398
399
10.3k
  ret = ioctl(reqfd, SIOGIFINDEX, &ifr);
400
401
10.3k
  require_string(ret >= 0, bail, strerror(errno));
402
403
0
  rt.rtmsg_ifindex = ifr.ifr_ifindex;
404
405
0
  ret = ioctl(reqfd, SIOCADDRT, &rt);
406
407
0
  require_quiet(ret == 0 || errno == EALREADY || errno == EEXIST, bail);
408
0
#endif
409
410
0
  ret = 0;
411
10.3k
bail:
412
413
10.3k
  return ret;
414
0
}
415
416
int
417
netif_mgmt_remove_ipv6_route(int reqfd, const char* if_name, const uint8_t route[16], int prefixlen, uint32_t metric)
418
8.43k
{
419
8.43k
  int ret = -1;
420
421
422
#ifdef __APPLE__
423
424
  /************* Remove ROUTE TODO *************/
425
426
#else
427
  /* Linux */
428
429
8.43k
  struct in6_rtmsg rt;
430
431
8.43k
  memset(&rt, 0, sizeof(struct in6_rtmsg));
432
433
8.43k
  memcpy(rt.rtmsg_dst.s6_addr, route, sizeof(struct in6_addr));
434
435
8.43k
  rt.rtmsg_dst_len = prefixlen;
436
8.43k
  rt.rtmsg_flags = RTF_UP;
437
8.43k
  rt.rtmsg_metric = metric;
438
439
8.43k
  if (prefixlen == 128) {
440
82
    rt.rtmsg_flags |= RTF_HOST;
441
82
  }
442
443
8.43k
  ret = netif_mgmt_get_ifindex(reqfd, if_name);
444
445
8.43k
  require_string(ret >= 0, bail, strerror(errno));
446
447
0
  rt.rtmsg_ifindex = ret;
448
449
0
  ret = ioctl(reqfd, SIOCDELRT, &rt);
450
451
0
  require_quiet(ret == 0 || errno == EALREADY || errno == EEXIST, bail);
452
453
0
#endif
454
455
0
  ret = 0;
456
8.43k
bail:
457
8.43k
  return ret;
458
0
}
459
460
int
461
netif_mgmt_join_ipv6_multicast_address(int reqfd, const char* if_name, const uint8_t addr[16])
462
0
{
463
0
  int ret = -1;
464
0
  struct ipv6_mreq imreq;
465
0
  unsigned int value = 1;
466
0
  memset(&imreq, 0, sizeof(imreq));
467
468
0
  require(reqfd >= 0, bail);
469
470
0
  memcpy(&imreq.ipv6mr_multiaddr.s6_addr, addr, 16);
471
472
0
  value = 1;
473
0
  ret = setsockopt(
474
0
    reqfd,
475
0
    IPPROTO_IPV6,
476
0
    IPV6_MULTICAST_LOOP,
477
0
    &value,
478
0
    sizeof(value)
479
0
  );
480
0
  require_noerr(ret, bail);
481
482
0
  ret = netif_mgmt_get_ifindex(reqfd, if_name);
483
0
  require_string(ret >= 0, bail, strerror(errno));
484
485
0
  imreq.ipv6mr_interface = ret;
486
487
0
  ret = setsockopt(
488
0
    reqfd,
489
0
    IPPROTO_IPV6,
490
0
    IPV6_JOIN_GROUP,
491
0
    &imreq,
492
0
    sizeof(imreq)
493
0
  );
494
0
  require_noerr(ret, bail);
495
496
0
bail:
497
0
  return ret;
498
0
}
499
500
int
501
netif_mgmt_leave_ipv6_multicast_address(int reqfd, const char* if_name, const uint8_t addr[16])
502
3.56k
{
503
3.56k
  int ret = -1;
504
3.56k
  struct ipv6_mreq imreq;
505
3.56k
  unsigned int value = 1;
506
3.56k
  memset(&imreq, 0, sizeof(imreq));
507
508
3.56k
  require(reqfd >= 0, bail);
509
510
0
  memcpy(&imreq.ipv6mr_multiaddr.s6_addr, addr, 16);
511
512
0
  value = 1;
513
0
  ret = setsockopt(
514
0
    reqfd,
515
0
    IPPROTO_IPV6,
516
0
    IPV6_MULTICAST_LOOP,
517
0
    &value,
518
0
    sizeof(value)
519
0
  );
520
0
  require_noerr(ret, bail);
521
522
0
  ret = netif_mgmt_get_ifindex(reqfd, if_name);
523
0
  require_string(ret >= 0, bail, strerror(errno));
524
525
0
  imreq.ipv6mr_interface = ret;
526
527
0
  ret = setsockopt(
528
0
    reqfd,
529
0
    IPPROTO_IPV6,
530
0
    IPV6_LEAVE_GROUP,
531
0
    &imreq,
532
0
    sizeof(imreq)
533
0
  );
534
0
  require_noerr(ret, bail);
535
536
3.56k
bail:
537
3.56k
  return ret;
538
0
}