Coverage Report

Created: 2026-08-31 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/radvd/device-linux.c
Line
Count
Source
1
/*
2
 *
3
 *   Authors:
4
 *    Lars Fenneberg    <lf@elemental.net>
5
 *
6
 *   This software is Copyright 1996,1997 by the above mentioned author(s),
7
 *   All Rights Reserved.
8
 *
9
 *   The license which is distributed with this software in the file COPYRIGHT
10
 *   applies to this software. If your distribution is missing this file, you
11
 *   may request it from https://github.com/radvd-project/radvd/issues
12
 *
13
 */
14
15
#include "config.h"
16
#include "defaults.h"
17
#include "includes.h"
18
#include "netlink.h"
19
#include "pathnames.h"
20
#include "radvd.h"
21
22
#ifndef IPV6_ADDR_LINKLOCAL
23
#define IPV6_ADDR_LINKLOCAL 0x0020U
24
#endif
25
26
#ifndef ARPHRD_6LOWPAN
27
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
28
#endif
29
30
static char const *hwstr(unsigned short sa_family);
31
uint32_t get_interface_linkmtu(const char *);
32
33
/*
34
 * this function gets the hardware type and address of an interface,
35
 * determines the link layer token length and checks it against
36
 * the defined prefixes
37
 */
38
int update_device_info(int sock, struct Interface *iface)
39
0
{
40
0
  struct ifreq ifr;
41
0
  int ra_option_size_step = 0;
42
0
  memset(&ifr, 0, sizeof(ifr));
43
0
  strlcpy(ifr.ifr_name, iface->props.name, sizeof(ifr.ifr_name));
44
45
0
  if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
46
0
    flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed on %s: %s", iface->props.name, strerror(errno));
47
0
    return -1;
48
0
  }
49
50
0
  iface->sllao.if_maxmtu = ifr.ifr_mtu;
51
0
  dlog(LOG_DEBUG, 3, "%s mtu: %d", iface->props.name, ifr.ifr_mtu);
52
53
  /* RFC 2460: 5. Packet Size Issues */
54
  /* Get the smallest MTU between the SIOCGIFMTU value and the protocol MTU
55
   * /proc/sys/net/ipv6/conf/eth0/mtu
56
   * Because the protocol MTU _may_ be different than the physical link MTU
57
   *
58
   * If Link-layer MTU <= 1280: use 1280 (enforced by iface->AdvRAMTU)
59
   * If Link-layer MTU > 1280: use the lower of:
60
   *   - RA MTU
61
   *   - link-layer MTU
62
   *   - per-protocol MTU
63
   */
64
65
0
  iface->props.max_ra_option_size = iface->AdvRAMTU;
66
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
67
0
  iface->props.max_ra_option_size = MIN(iface->props.max_ra_option_size, MAX(iface->sllao.if_maxmtu, RFC2460_MIN_MTU));
68
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
69
0
  iface->props.max_ra_option_size =
70
0
      MIN(iface->props.max_ra_option_size, MAX(get_interface_linkmtu(iface->props.name), RFC2460_MIN_MTU));
71
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
72
73
0
  if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
74
0
    flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed on %s: %s", iface->props.name, strerror(errno));
75
0
    return -1;
76
0
  }
77
78
0
  dlog(LOG_DEBUG, 3, "%s hardware type: %s", iface->props.name, hwstr(ifr.ifr_hwaddr.sa_family));
79
80
0
  switch (ifr.ifr_hwaddr.sa_family) {
81
0
  case ARPHRD_ETHER:
82
0
    iface->sllao.if_hwaddr_len = 48;
83
0
    iface->sllao.if_prefix_len = 64;
84
0
    char hwaddr[3 * 6];
85
0
    sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr.ifr_hwaddr.sa_data[0],
86
0
      (unsigned char)ifr.ifr_hwaddr.sa_data[1], (unsigned char)ifr.ifr_hwaddr.sa_data[2],
87
0
      (unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4],
88
0
      (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
89
0
    dlog(LOG_DEBUG, 3, "%s hardware address: %s", iface->props.name, hwaddr);
90
0
    iface->props.max_ra_option_size -= 14; /* RFC 2464 */
91
0
    break;
92
0
#ifdef ARPHRD_FDDI
93
0
  case ARPHRD_FDDI:
94
0
    iface->sllao.if_hwaddr_len = 48;
95
0
    iface->sllao.if_prefix_len = 64;
96
0
    iface->props.max_ra_option_size -= 22; /* RFC 2109 */
97
0
    break;
98
0
#endif /* ARPHDR_FDDI */
99
0
#ifdef ARPHRD_ARCNET
100
0
  case ARPHRD_ARCNET:
101
0
    iface->sllao.if_hwaddr_len = 8;
102
0
    iface->sllao.if_prefix_len = -1;
103
0
    iface->sllao.if_maxmtu = -1;
104
    /* RFC1201: fragmentation handled at a lower layer.
105
     * native packet size is 256-512 bytes */
106
0
    iface->props.max_ra_option_size -= 0;
107
0
    break;
108
0
#endif /* ARPHDR_ARCNET */
109
0
  case ARPHRD_6LOWPAN:
110
#ifdef HAVE_NETLINK
111
    /* hwaddr length differs on some L2 type lets detect them */
112
    iface->sllao.if_hwaddr_len = netlink_get_device_addr_len(iface);
113
    if (iface->sllao.if_hwaddr_len != -1) {
114
      iface->sllao.if_hwaddr_len *= 8;
115
      iface->sllao.if_prefix_len = 64;
116
    } else {
117
      iface->sllao.if_prefix_len = -1;
118
    }
119
#else
120
0
    iface->sllao.if_hwaddr_len = -1;
121
0
    iface->sllao.if_prefix_len = -1;
122
0
#endif
123
    /* RFC4944: fragmentation handled at a lower layer.
124
     * native packet size maxes at 127 bytes */
125
0
    iface->props.max_ra_option_size -= 0;
126
0
    break;
127
0
  default:
128
0
    iface->sllao.if_hwaddr_len = -1;
129
0
    iface->sllao.if_prefix_len = -1;
130
0
    iface->sllao.if_maxmtu = -1;
131
    /* Assume fragmentation handled at a lower layer. */
132
0
    iface->props.max_ra_option_size -= 0;
133
0
    break;
134
0
  }
135
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
136
137
0
  dlog(LOG_DEBUG, 3, "%s link layer token length: %d", iface->props.name, iface->sllao.if_hwaddr_len);
138
139
0
  dlog(LOG_DEBUG, 3, "%s prefix length: %d", iface->props.name, iface->sllao.if_prefix_len);
140
141
0
  if (iface->sllao.if_hwaddr_len != -1) {
142
0
    unsigned int if_hwaddr_len_bytes = (iface->sllao.if_hwaddr_len + 7) >> 3;
143
144
0
    if (if_hwaddr_len_bytes > sizeof(iface->sllao.if_hwaddr)) {
145
0
      flog(LOG_ERR, "%s address length too big: %d", iface->props.name, if_hwaddr_len_bytes);
146
0
      return -2;
147
0
    }
148
0
    memcpy(iface->sllao.if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes);
149
150
0
    char zero[sizeof(iface->props.if_addr)];
151
0
    memset(zero, 0, sizeof(zero));
152
0
    if (!memcmp(iface->sllao.if_hwaddr, zero, if_hwaddr_len_bytes))
153
0
      flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!", iface->props.name);
154
0
  }
155
156
0
  struct AdvPrefix *prefix = iface->AdvPrefixList;
157
0
  while (prefix) {
158
0
    if ((iface->sllao.if_prefix_len != -1) && (iface->sllao.if_prefix_len != prefix->PrefixLen)) {
159
0
      flog(LOG_WARNING, "%s prefix length should be: %d", iface->props.name, iface->sllao.if_prefix_len);
160
0
    }
161
162
0
    prefix = prefix->next;
163
0
  }
164
165
  // Regardless of link-layer, every RA message will have an IPV6 header & RA header
166
0
  iface->props.max_ra_option_size -= sizeof(struct ip6_hdr);
167
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
168
0
  iface->props.max_ra_option_size -= sizeof(struct nd_router_advert);
169
0
  dlog(LOG_DEBUG, 5, "%s max ra option size (step %d): %d",iface->props.name, ra_option_size_step++, iface->props.max_ra_option_size);
170
171
0
  dlog(LOG_DEBUG, 3, "%s max ra option size (final): %d", iface->props.name, iface->props.max_ra_option_size);
172
173
0
  return 0;
174
0
}
175
176
int setup_allrouters_membership(int sock, struct Interface *iface)
177
0
{
178
0
  struct ipv6_mreq mreq;
179
180
0
  memset(&mreq, 0, sizeof(mreq));
181
0
  mreq.ipv6mr_interface = iface->props.if_index;
182
183
  /* ipv6-allrouters: ff02::2 */
184
0
  mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
185
0
  mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
186
187
0
  if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
188
    /* linux-2.6.12-bk4 returns error with HUP signal but keep listening */
189
0
    if (errno != EADDRINUSE) {
190
0
      flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->props.name);
191
0
      return -1;
192
0
    }
193
0
  }
194
195
0
  return 0;
196
0
}
197
198
int cleanup_allrouters_membership(int sock, struct Interface *iface)
199
0
{
200
0
  struct ipv6_mreq mreq;
201
202
0
  memset(&mreq, 0, sizeof(mreq));
203
0
  mreq.ipv6mr_interface = iface->props.if_index;
204
205
  /* ipv6-allrouters: ff02::2 */
206
0
  mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
207
0
  mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
208
209
0
  setsockopt(sock, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
210
211
0
  return 0;
212
0
}
213
214
uint32_t get_interface_linkmtu(const char *iface)
215
0
{
216
0
  int value;
217
0
  FILE *fp = NULL;
218
0
  char proc_path[sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ];
219
220
0
  snprintf(proc_path, sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ, PROC_SYS_IP6_LINKMTU, iface);
221
222
0
  fp = fopen(proc_path, "r");
223
0
  if (fp) {
224
0
    int rc = fscanf(fp, "%d", &value);
225
0
    if (rc != 1) {
226
0
      flog(LOG_ERR, "cannot read value from %s: %s", proc_path, strerror(errno));
227
0
      exit(1);
228
0
    }
229
0
    fclose(fp);
230
0
  } else {
231
0
    flog(LOG_DEBUG, "Correct IPv6 MTU entry not found, "
232
0
        "perhaps the procfs is disabled, "
233
0
        "or the kernel interface has changed?");
234
0
    value = 1280; /* RFC2460: section 5 */
235
0
  }
236
237
0
  return value;
238
0
}
239
240
0
int set_interface_linkmtu(const char *iface, uint32_t mtu) { return privsep_interface_linkmtu(iface, mtu); }
241
242
0
int set_interface_curhlim(const char *iface, uint8_t hlim) { return privsep_interface_curhlim(iface, hlim); }
243
244
0
int set_interface_reachtime(const char *iface, uint32_t rtime) { return privsep_interface_reachtime(iface, rtime); }
245
246
0
int set_interface_retranstimer(const char *iface, uint32_t rettimer) { return privsep_interface_retranstimer(iface, rettimer); }
247
248
int check_ip6_iface_forwarding(const char *iface)
249
0
{
250
0
  int value = -1;
251
0
  FILE *fp = NULL;
252
0
  char path[sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ];
253
254
0
  snprintf(path, sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ, PROC_SYS_IP6_IFACE_FORWARDING, iface);
255
256
0
  fp = fopen(path, "r");
257
0
  if (fp) {
258
0
    int rc = fscanf(fp, "%d", &value);
259
0
    if (rc != 1) {
260
0
      flog(LOG_ERR, "cannot read value from %s: %s", path, strerror(errno));
261
0
      exit(1);
262
0
    }
263
0
    fclose(fp);
264
0
  } else {
265
0
    flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry for interface "
266
0
        "not found, perhaps the procfs is disabled, "
267
0
        "or the kernel interface has changed?");
268
0
    value = -1;
269
0
  }
270
271
0
  return value;
272
0
}
273
274
int check_ip6_forwarding(void)
275
0
{
276
0
  int value;
277
0
  FILE *fp = NULL;
278
279
0
  fp = fopen(PROC_SYS_IP6_FORWARDING, "r");
280
0
  if (fp) {
281
0
    int rc = fscanf(fp, "%d", &value);
282
0
    if (rc != 1) {
283
0
      flog(LOG_ERR, "cannot read value from %s: %s", PROC_SYS_IP6_FORWARDING, strerror(errno));
284
0
      exit(1);
285
0
    }
286
0
    fclose(fp);
287
0
  } else {
288
0
    flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, "
289
0
        "perhaps the procfs is disabled, "
290
0
        "or the kernel interface has changed?");
291
0
    value = -1;
292
0
  }
293
294
#ifdef HAVE_SYSCTL
295
  int forw_sysctl[] = {SYSCTL_IP6_FORWARDING};
296
  size_t size = sizeof(value);
297
  if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl) / sizeof(forw_sysctl[0]), &value, &size, NULL, 0) < 0) {
298
    flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, "
299
        "perhaps the kernel interface has changed?");
300
    return 0; /* this is of advisory value only */
301
  }
302
#endif
303
304
  /* Linux allows the forwarding value to be either 1 or 2.
305
   * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/ip-sysctl.txt?id=ae8abfa00efb8ec550f772cbd1e1854977d06212#n1078
306
   *
307
   * The value 2 indicates forwarding is enabled and that *AS* *WELL* router solicitations are being done.
308
   *
309
   * Which is sometimes used on routers performing RS on their WAN (ppp, etc.) links
310
   */
311
0
  static int warned = 0;
312
0
  if (!warned && value != 1 && value != 2) {
313
0
    warned = 1;
314
0
    flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1 or 2", value);
315
0
    return -1;
316
0
  }
317
318
0
  return 0;
319
0
}
320
321
static char const *hwstr(unsigned short sa_family)
322
0
{
323
0
  char const *rc = 0;
324
325
0
  switch (sa_family) {
326
0
  case ARPHRD_NETROM:
327
0
    rc = "ARPHRD_NETROM";
328
0
    break;
329
0
  case ARPHRD_ETHER:
330
0
    rc = "ARPHRD_ETHER";
331
0
    break;
332
0
  case ARPHRD_EETHER:
333
0
    rc = "ARPHRD_EETHER";
334
0
    break;
335
0
  case ARPHRD_AX25:
336
0
    rc = "ARPHRD_AX25";
337
0
    break;
338
0
  case ARPHRD_PRONET:
339
0
    rc = "ARPHRD_PRONET";
340
0
    break;
341
0
  case ARPHRD_CHAOS:
342
0
    rc = "ARPHRD_CHAOS";
343
0
    break;
344
0
  case ARPHRD_IEEE802:
345
0
    rc = "ARPHRD_IEEE802";
346
0
    break;
347
0
  case ARPHRD_APPLETLK:
348
0
    rc = "ARPHRD_APPLETLK";
349
0
    break;
350
0
  case ARPHRD_DLCI:
351
0
    rc = "ARPHRD_DLCI";
352
0
    break;
353
0
  case ARPHRD_ATM:
354
0
    rc = "ARPHRD_ATM";
355
0
    break;
356
0
  case ARPHRD_METRICOM:
357
0
    rc = "ARPHRD_METRICOM";
358
0
    break;
359
0
  case ARPHRD_IEEE1394:
360
0
    rc = "ARPHRD_IEEE1394";
361
0
    break;
362
0
  case ARPHRD_EUI64:
363
0
    rc = "ARPHRD_EUI64";
364
0
    break;
365
0
  case ARPHRD_INFINIBAND:
366
0
    rc = "ARPHRD_INFINIBAND";
367
0
    break;
368
0
  case ARPHRD_SLIP:
369
0
    rc = "ARPHRD_SLIP";
370
0
    break;
371
0
  case ARPHRD_CSLIP:
372
0
    rc = "ARPHRD_CSLIP";
373
0
    break;
374
0
  case ARPHRD_SLIP6:
375
0
    rc = "ARPHRD_SLIP6";
376
0
    break;
377
0
  case ARPHRD_CSLIP6:
378
0
    rc = "ARPHRD_CSLIP6";
379
0
    break;
380
0
  case ARPHRD_RSRVD:
381
0
    rc = "ARPHRD_RSRVD";
382
0
    break;
383
0
  case ARPHRD_ADAPT:
384
0
    return "ARPHRD_ADAPT";
385
0
    break;
386
0
  case ARPHRD_ROSE:
387
0
    rc = "ARPHRD_ROSE";
388
0
    break;
389
0
  case ARPHRD_X25:
390
0
    rc = "ARPHRD_X25";
391
0
    break;
392
0
  case ARPHRD_HWX25:
393
0
    rc = "ARPHRD_HWX25";
394
0
    break;
395
0
  case ARPHRD_PPP:
396
0
    rc = "ARPHRD_PPP";
397
0
    break;
398
0
  case ARPHRD_CISCO:
399
0
    rc = "ARPHRD_CISCO";
400
0
    break;
401
0
  case ARPHRD_LAPB:
402
0
    rc = "ARPHRD_LAPB";
403
0
    break;
404
0
  case ARPHRD_DDCMP:
405
0
    rc = "ARPHRD_DDCMP";
406
0
    break;
407
0
  case ARPHRD_RAWHDLC:
408
0
    rc = "ARPHRD_RAWHDLC";
409
0
    break;
410
0
  case ARPHRD_TUNNEL:
411
0
    rc = "ARPHRD_TUNNEL";
412
0
    break;
413
0
  case ARPHRD_TUNNEL6:
414
0
    rc = "ARPHRD_TUNNEL6";
415
0
    break;
416
0
  case ARPHRD_FRAD:
417
0
    rc = "ARPHRD_FRAD";
418
0
    break;
419
0
  case ARPHRD_SKIP:
420
0
    rc = "ARPHRD_SKIP";
421
0
    break;
422
0
  case ARPHRD_LOOPBACK:
423
0
    rc = "ARPHRD_LOOPBACK";
424
0
    break;
425
0
  case ARPHRD_LOCALTLK:
426
0
    rc = "ARPHRD_LOCALTLK";
427
0
    break;
428
0
  case ARPHRD_BIF:
429
0
    rc = "ARPHRD_BIF";
430
0
    break;
431
0
  case ARPHRD_SIT:
432
0
    rc = "ARPHRD_SIT";
433
0
    break;
434
0
  case ARPHRD_IPDDP:
435
0
    rc = "ARPHRD_IPDDP";
436
0
    break;
437
0
  case ARPHRD_IPGRE:
438
0
    rc = "ARPHRD_IPGRE";
439
0
    break;
440
0
  case ARPHRD_PIMREG:
441
0
    rc = "ARPHRD_PIMREG";
442
0
    break;
443
0
  case ARPHRD_HIPPI:
444
0
    rc = "ARPHRD_HIPPI";
445
0
    break;
446
0
  case ARPHRD_ASH:
447
0
    rc = "ARPHRD_ASH";
448
0
    break;
449
0
  case ARPHRD_ECONET:
450
0
    rc = "ARPHRD_ECONET";
451
0
    break;
452
0
  case ARPHRD_IRDA:
453
0
    rc = "ARPHRD_IRDA";
454
0
    break;
455
0
  case ARPHRD_FCPP:
456
0
    rc = "ARPHRD_FCPP";
457
0
    break;
458
0
  case ARPHRD_FCAL:
459
0
    rc = "ARPHRD_FCAL";
460
0
    break;
461
0
  case ARPHRD_FCPL:
462
0
    rc = "ARPHRD_FCPL";
463
0
    break;
464
0
  case ARPHRD_FCFABRIC:
465
0
    rc = "ARPHRD_FCFABRIC";
466
0
    break;
467
0
  case ARPHRD_IEEE802_TR:
468
0
    rc = "ARPHRD_IEEE802_TR";
469
0
    break;
470
0
  case ARPHRD_IEEE80211:
471
0
    rc = "ARPHRD_IEEE80211";
472
0
    break;
473
0
  case ARPHRD_IEEE80211_PRISM:
474
0
    rc = "ARPHRD_IEEE80211_PRISM";
475
0
    break;
476
0
  case ARPHRD_IEEE80211_RADIOTAP:
477
0
    rc = "ARPHRD_IEEE80211_RADIOTAP";
478
0
    break;
479
0
  case ARPHRD_IEEE802154:
480
0
    rc = "ARPHRD_IEEE802154";
481
0
    break;
482
0
#if ARPHRD_IEEE802154_MONITOR
483
0
  case ARPHRD_IEEE802154_MONITOR:
484
0
    rc = "ARPHRD_IEEE802154_MONITOR";
485
0
    break;
486
#elif ARPHRD_IEEE802154_PHY
487
  case ARPHRD_IEEE802154_PHY:
488
    rc = "ARPHRD_IEEE802154_PHY";
489
    break;
490
#endif
491
0
  case ARPHRD_6LOWPAN:
492
0
    rc = "ARPHRD_6LOWPAN";
493
0
    break;
494
0
  case ARPHRD_VOID:
495
0
    rc = "ARPHRD_VOID";
496
0
    break;
497
0
  case ARPHRD_NONE:
498
0
    rc = "ARPHRD_NONE";
499
0
    break;
500
0
  default:
501
0
    rc = "unknown";
502
0
    break;
503
0
  }
504
505
0
  return rc;
506
0
}