Coverage Report

Created: 2026-02-14 07:03

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