• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 11783254300

11 Nov 2024 05:23PM UTC coverage: 45.767% (+0.09%) from 45.677%
11783254300

push

github

web-flow
Merge pull request #1978 from DMXControl/add_nodle_r4s

Add DMXControl Projects e.V. Nodle R4S

7798 of 17938 branches covered (43.47%)

0 of 1 new or added line in 1 file covered. (0.0%)

721 existing lines in 11 files now uncovered.

22344 of 48821 relevant lines covered (45.77%)

64.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.89
/common/network/NetworkUtils.cpp
1
/*
2
 * This library is free software; you can redistribute it and/or
3
 * modify it under the terms of the GNU Lesser General Public
4
 * License as published by the Free Software Foundation; either
5
 * version 2.1 of the License, or (at your option) any later version.
6
 *
7
 * This library is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10
 * Lesser General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU Lesser General Public
13
 * License along with this library; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
 *
16
 * NetworkUtils.cpp
17
 * Abstract various network functions.
18
 * Copyright (C) 2005 Simon Newton
19
 */
20

21
#include "ola/network/NetworkUtils.h"
22

23
#if HAVE_CONFIG_H
24
#include <config.h>
25
#endif  // HAVE_CONFIG_H
26

27

28
#ifdef _WIN32
29
typedef uint32_t in_addr_t;
30
// Iphlpapi.h depends on Winsock2.h
31
#define WIN_32_LEAN_AND_MEAN
32
#include <ola/win/CleanWinSock2.h>
33
#include <Iphlpapi.h>
34
#if HAVE_WINERROR_H
35
#include <winerror.h>
36
#endif  // HAVE_WINERROR_H
37
#else
38
#include <netinet/in.h>
39
#endif  // _WIN32
40

41
#ifdef HAVE_RESOLV_H
42
#include <resolv.h>
43
#endif  // HAVE_RESOLV_H
44

45
#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
46
#define USE_NETLINK_FOR_DEFAULT_ROUTE 1
47
#include <linux/netlink.h>
48
#include <linux/rtnetlink.h>
49
#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NET_ROUTE_H) && \
50
      defined(HAVE_DECL_PF_ROUTE) && defined(HAVE_DECL_NET_RT_DUMP)
51
#define USE_SYSCTL_FOR_DEFAULT_ROUTE 1
52
#include <net/route.h>
53
#ifdef HAVE_SYS_PARAM_H
54
#include <sys/param.h>
55
#endif  // HAVE_SYS_PARAM_H
56
#include <sys/sysctl.h>
57
#else
58
// TODO(Peter): Do something else if we don't have Netlink/on Windows
59
#endif  // defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
60

61
#ifdef HAVE_ENDIAN_H
62
#include <endian.h>
63
#endif  // HAVE_ENDIAN_H
64
#include <errno.h>
65
#include <limits.h>
66
#include <stdlib.h>
67
#include <string.h>
68
#include <unistd.h>
69
#include <iomanip>
70
#include <memory>
71
#include <sstream>
72
#include <string>
73
#include <vector>
74
#include "common/network/NetworkUtilsInternal.h"
75
#include "ola/Callback.h"
76
#include "ola/Logging.h"
77
#include "ola/StringUtils.h"
78
#include "ola/math/Random.h"
79
#include "ola/network/Interface.h"
80
#include "ola/network/MACAddress.h"
81
#include "ola/network/SocketCloser.h"
82

83

84
namespace ola {
85
namespace network {
86

87
using std::string;
88
using std::vector;
89
using ola::network::Interface;
90

91
namespace {
92

93
inline bool IsBigEndian() {
94
// Some versions of NetBSD have endian.h but not __BIG_ENDIAN
95
#if defined(HAVE_ENDIAN_H) && defined(__BIG_ENDIAN)
96
  return BYTE_ORDER == __BIG_ENDIAN;
97
#else
98
#ifdef _WIN32
99
  // Windows currently only runs in little-endian mode, but that might change
100
  // on future devices. Since there is no BYTE_ORDER define, we use this
101
  // little trick from http://esr.ibiblio.org/?p=5095
102
  return (*(uint16_t*)"\0\xff" < 0x100);  // NOLINT(readability/casting)
103
#else
104
  return BYTE_ORDER == BIG_ENDIAN;
105
#endif  // _WIN32
106
#endif  // defined(HAVE_ENDIAN_H) && defined(__BIG_ENDIAN)
107
}
108

109
inline uint64_t ByteSwap64(uint64_t value) {
110
  return ((value & 0x00000000000000ff) << 56) |
111
         ((value & 0x000000000000ff00) << 40) |
112
         ((value & 0x0000000000ff0000) << 24) |
113
         ((value & 0x00000000ff000000) << 8) |
114
         ((value & 0x000000ff00000000) >> 8) |
115
         ((value & 0x0000ff0000000000) >> 24) |
116
         ((value & 0x00ff000000000000) >> 40) |
117
         ((value & 0xff00000000000000) >> 56);
118
}
119

120
inline uint32_t ByteSwap32(uint32_t value) {
121
  return ((value & 0x000000ff) << 24) |
122
         ((value & 0x0000ff00) << 8) |
123
         ((value & 0x00ff0000) >> 8) |
124
         ((value & 0xff000000) >> 24);
125
}
126

127
inline uint32_t ByteSwap16(uint16_t value) {
128
  return ((value & 0xff) << 8) | ((value & 0xff00) >> 8);
129
}
130
}  // namespace
131

132
unsigned int SockAddrLen(const struct sockaddr &sa) {
18✔
133
#ifdef HAVE_SOCKADDR_SA_LEN
134
  return sa.sa_len;
135
#else
136
  switch (sa.sa_family) {
18✔
137
    case AF_INET:
138
      return sizeof(struct sockaddr_in);
139
#ifdef IPV6
140
    case AF_INET6:
141
      return sizeof(struct sockaddr_in6);
142
#endif  // IPV6
143
#ifdef HAVE_SOCKADDR_DL_STRUCT
144
    case AF_LINK:
145
      return sizeof(struct sockaddr_dl);
146
#endif  // HAVE_SOCKADDR_DL_STRUCT
UNCOV
147
    default:
×
UNCOV
148
      OLA_WARN << "Can't determine size of sockaddr: " << sa.sa_family;
×
UNCOV
149
      return sizeof(struct sockaddr);
×
150
  }
151
#endif  // HAVE_SOCKADDR_SA_LEN
152
}
153

154
uint16_t NetworkToHost(uint16_t value) {
167✔
155
  return ntohs(value);
167✔
156
}
157

158
uint32_t NetworkToHost(uint32_t value) {
253✔
159
  return ntohl(value);
253✔
160
}
161

162
uint64_t NetworkToHost(uint64_t value) {
2✔
163
#ifdef HAVE_ENDIAN_H
164
  return be64toh(value);
2✔
165
#else
166
#error "No be64toh for NetworkToHost, please report this."
167
#endif  // HAVE_ENDIAN_H
168
}
169

170
int16_t NetworkToHost(int16_t value) {
2✔
171
  return ntohs(value);
2✔
172
}
173

174
int32_t NetworkToHost(int32_t value) {
2✔
175
  return ntohl(value);
2✔
176
}
177

178
int64_t NetworkToHost(int64_t value) {
1✔
179
#ifdef HAVE_ENDIAN_H
180
  return be64toh(value);
1✔
181
#else
182
#error "No be64toh for NetworkToHost, please report this."
183
#endif  // HAVE_ENDIAN_H
184
}
185

186
uint16_t HostToNetwork(uint16_t value) {
467✔
187
  return htons(value);
467✔
188
}
189

190
int16_t HostToNetwork(int16_t value) {
3✔
191
  return htons(value);
3✔
192
}
193

194
uint32_t HostToNetwork(uint32_t value) {
126✔
195
  return htonl(value);
126✔
196
}
197

198
int32_t HostToNetwork(int32_t value) {
44✔
199
  return htonl(value);
44✔
200
}
201

202
uint64_t HostToNetwork(uint64_t value) {
2✔
203
#ifdef HAVE_ENDIAN_H
204
  return htobe64(value);
2✔
205
#else
206
#error "No htobe64 for HostToNetwork, please report this."
207
#endif  // HAVE_ENDIAN_H
208
}
209

210
int64_t HostToNetwork(int64_t value) {
1✔
211
#ifdef HAVE_ENDIAN_H
212
  return htobe64(value);
1✔
213
#else
214
#error "No htobe64 for HostToNetwork, please report this."
215
#endif  // HAVE_ENDIAN_H
216
}
217

218
uint16_t HostToLittleEndian(uint16_t value) {
133✔
219
  if (IsBigEndian()) {
133✔
220
    return ByteSwap16(value);
221
  } else {
222
    return value;
133✔
223
  }
224
}
225

226
int16_t HostToLittleEndian(int16_t value) {
2✔
227
  if (IsBigEndian()) {
2✔
228
    return ByteSwap16(value);
229
  } else {
230
    return value;
2✔
231
  }
232
}
233

234
uint32_t HostToLittleEndian(uint32_t value) {
2✔
235
  if (IsBigEndian()) {
2✔
236
    return ByteSwap32(value);
237
  } else {
238
    return value;
2✔
239
  }
240
}
241

242
int32_t HostToLittleEndian(int32_t value) {
2✔
243
  if (IsBigEndian()) {
2✔
244
    return ByteSwap32(value);
245
  } else {
246
    return value;
2✔
247
  }
248
}
249

250
uint64_t HostToLittleEndian(uint64_t value) {
2✔
251
  if (IsBigEndian()) {
2✔
252
    return ByteSwap64(value);
253
  } else {
254
    return value;
2✔
255
  }
256
}
257

258
int64_t HostToLittleEndian(int64_t value) {
2✔
259
  if (IsBigEndian()) {
2✔
260
    return ByteSwap64(value);
261
  } else {
262
    return value;
2✔
263
  }
264
}
265

266
uint16_t LittleEndianToHost(uint16_t value) {
114✔
267
  if (IsBigEndian()) {
114✔
268
    return ByteSwap16(value);
269
  } else {
270
    return value;
114✔
271
  }
272
}
273

274

275
int16_t LittleEndianToHost(int16_t value) {
2✔
276
  if (IsBigEndian()) {
2✔
277
    return ByteSwap16(value);
278
  } else {
279
    return value;
2✔
280
  }
281
}
282

283

284
uint32_t LittleEndianToHost(uint32_t value) {
14✔
285
  if (IsBigEndian()) {
14✔
286
    return ByteSwap32(value);
287
  } else {
288
    return value;
14✔
289
  }
290
}
291

292

293
int32_t LittleEndianToHost(int32_t value) {
2✔
294
  if (IsBigEndian()) {
2✔
295
    return ByteSwap32(value);
296
  } else {
297
    return value;
2✔
298
  }
299
}
300

301

302
uint64_t LittleEndianToHost(uint64_t value) {
2✔
303
  if (IsBigEndian()) {
2✔
304
    return ByteSwap64(value);
305
  } else {
306
    return value;
2✔
307
  }
308
}
309

310

311
int64_t LittleEndianToHost(int64_t value) {
2✔
312
  if (IsBigEndian()) {
2✔
313
    return ByteSwap64(value);
314
  } else {
315
    return value;
2✔
316
  }
317
}
318

319
string HostnameFromFQDN(const string &fqdn) {
7✔
320
  string::size_type first_dot = fqdn.find_first_of(".");
7✔
321
  if (first_dot == string::npos) {
7✔
322
    return fqdn;
4✔
323
  }
324
  return fqdn.substr(0, first_dot);  // Don't return the dot itself
3✔
325
}
326

327
string DomainNameFromFQDN(const string &fqdn) {
5✔
328
  string::size_type first_dot = string::npos;
5✔
329
  first_dot = fqdn.find_first_of(".");
5✔
330
  if (first_dot == string::npos) {
5✔
331
    return "";
2✔
332
  }
333
  return fqdn.substr(first_dot + 1);  // Don't return the dot itself
3✔
334
}
335

336

UNCOV
337
string DomainName() {
×
UNCOV
338
  return DomainNameFromFQDN(FQDN());
×
339
}
340

341

342
string FQDN() {
3✔
343
#ifdef _POSIX_HOST_NAME_MAX
344
  char hostname[_POSIX_HOST_NAME_MAX];
3✔
345
#else
346
  char hostname[256];
347
#endif  // _POSIX_HOST_NAME_MAX
348
  int ret = gethostname(hostname, sizeof(hostname));
3✔
349

350
  if (ret) {
3✔
UNCOV
351
    OLA_WARN << "gethostname failed: " << strerror(errno);
×
UNCOV
352
    return "";
×
353
  }
354
  return hostname;
3✔
355
}
356

357

UNCOV
358
string FullHostname() {
×
UNCOV
359
  return FQDN();
×
360
}
361

362

363
string Hostname() {
2✔
364
  return HostnameFromFQDN(FQDN());
4✔
365
}
366

367

368
bool NameServers(vector<IPV4Address> *name_servers) {
1✔
369
#if HAVE_DECL_RES_NINIT
370
  struct __res_state res;
1✔
371
  memset(&res, 0, sizeof(struct __res_state));
1✔
372

373
  // Init the resolver info each time so it's always current for the RDM
374
  // responders in case we've set it via RDM too
375
  if (res_ninit(&res) != 0) {
1✔
UNCOV
376
    OLA_WARN << "Error getting nameservers via res_ninit";
×
UNCOV
377
    return false;
×
378
  }
379

380
  for (int32_t i = 0; i < res.nscount; i++) {
2✔
381
    IPV4Address addr = IPV4Address(res.nsaddr_list[i].sin_addr.s_addr);
1✔
382
    OLA_DEBUG << "Found Nameserver " << i << ": " << addr;
1✔
383
    name_servers->push_back(addr);
1✔
384
  }
385

386
  res_nclose(&res);
1✔
387
#elif defined(_WIN32)
388
  ULONG size = sizeof(FIXED_INFO);
389
  PFIXED_INFO fixed_info = NULL;
390
  while (1) {
391
    fixed_info = reinterpret_cast<PFIXED_INFO>(new uint8_t[size]);
392
    DWORD result = GetNetworkParams(fixed_info, &size);
393
    if (result == ERROR_SUCCESS) {
394
      break;
395
    }
396

397
    if (result != ERROR_BUFFER_OVERFLOW) {
398
      OLA_WARN << "GetNetworkParams failed with: " << GetLastError();
399
      return false;
400
    }
401

402
    delete[] fixed_info;
403
  }
404

405
  IP_ADDR_STRING* addr = &(fixed_info->DnsServerList);
406
  for (; addr; addr = addr->Next) {
407
    IPV4Address ipv4addr = IPV4Address(inet_addr(addr->IpAddress.String));
408
    OLA_DEBUG << "Found nameserver: " << ipv4addr;
409
    name_servers->push_back(ipv4addr);
410
  }
411

412
  delete[] fixed_info;
413
#else
414
  // Init the resolver info each time so it's always current for the RDM
415
  // responders in case we've set it via RDM too
416
  if (res_init() != 0) {
417
    OLA_WARN << "Error getting nameservers via res_init";
418
    return false;
419
  }
420

421
  for (int32_t i = 0; i < _res.nscount; i++) {
422
    IPV4Address addr = IPV4Address(_res.nsaddr_list[i].sin_addr.s_addr);
423
    OLA_DEBUG << "Found Nameserver " << i << ": " << addr;
424
    name_servers->push_back(addr);
425
  }
426
#endif  // HAVE_DECL_RES_NINIT
427

428
  return true;
1✔
429
}
430

431

432
#ifdef USE_SYSCTL_FOR_DEFAULT_ROUTE
433

434
/**
435
 * Try to extract an AF_INET address from a sockaddr. If successful, sa points
436
 * to the next sockaddr and true is returned.
437
 */
438
bool ExtractIPV4AddressFromSockAddr(const uint8_t **data,
439
                                    IPV4Address *ip) {
440
  const struct sockaddr *sa = reinterpret_cast<const struct sockaddr*>(*data);
441
  if (sa->sa_family != AF_INET) {
442
    return false;
443
  }
444

445
  *ip = IPV4Address(
446
      reinterpret_cast<const struct sockaddr_in*>(*data)->sin_addr.s_addr);
447
  *data += SockAddrLen(*sa);
448
  return true;
449
}
450

451
/**
452
 * Use sysctl() to get the default route
453
 */
454
static bool GetDefaultRouteWithSysctl(int32_t *if_index,
455
                                      IPV4Address *default_gateway) {
456
  int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
457

458
  size_t space_required;
459
  uint8_t *buffer = NULL;
460
  // loop until we know we've read all the data.
461
  while (1) {
462
    int ret = sysctl(mib, 6, NULL, &space_required, NULL, 0);
463

464
    if (ret < 0) {
465
      OLA_WARN << "sysctl({CTL_NET, PF_ROUTE, 0, 0, NET_RT_DUMP, 0}, 6, NULL) "
466
               << "failed: " << strerror(errno);
467
      return false;
468
    }
469
    buffer = new uint8_t[space_required];
470

471
    ret = sysctl(mib, 6, buffer, &space_required, NULL, 0);
472
    if (ret < 0) {
473
      delete[] buffer;
474
      if (errno == ENOMEM) {
475
        continue;
476
      } else {
477
        OLA_WARN
478
            << "sysctl({CTL_NET, PF_ROUTE, 0, 0, NET_RT_DUMP, 0}, 6, !NULL)"
479
            << " failed: " << strerror(errno);
480
        return false;
481
      }
482
    } else {
483
      break;
484
    }
485
  }
486

487
  const struct rt_msghdr *rtm = NULL;
488
  const uint8_t *end = buffer + space_required;
489

490
  for (const uint8_t *next = buffer; next < end; next += rtm->rtm_msglen) {
491
    rtm = reinterpret_cast<const struct rt_msghdr*>(next);
492
    if (rtm->rtm_version != RTM_VERSION) {
493
      OLA_WARN << "Old RTM_VERSION, was " << rtm->rtm_version << ", expected "
494
               << RTM_VERSION;
495
      continue;
496
    }
497

498
    const uint8_t *data_start = reinterpret_cast<const uint8_t*>(rtm + 1);
499

500
    IPV4Address dest, gateway, netmask;
501

502
    if (rtm->rtm_flags & RTA_DST) {
503
      if (!ExtractIPV4AddressFromSockAddr(&data_start, &dest)) {
504
        continue;
505
      }
506
    }
507

508
    if (rtm->rtm_flags & RTA_GATEWAY) {
509
      if (!ExtractIPV4AddressFromSockAddr(&data_start, &gateway)) {
510
        continue;
511
      }
512
    }
513

514
    if (rtm->rtm_flags & RTA_NETMASK) {
515
      if (!ExtractIPV4AddressFromSockAddr(&data_start, &netmask)) {
516
        continue;
517
      }
518
    }
519

520
    if (dest.IsWildcard() && netmask.IsWildcard()) {
521
      *default_gateway = gateway;
522
      *if_index = rtm->rtm_index;
523
      delete[] buffer;
524
      OLA_INFO << "Default gateway: " << *default_gateway << ", if_index: "
525
               << *if_index;
526
      return true;
527
    }
528
  }
529
  delete[] buffer;
530
  OLA_WARN << "No default route found";
531
  return true;
532
}
533
#elif defined(USE_NETLINK_FOR_DEFAULT_ROUTE)
534

535
/**
536
 * Handle a netlink message. If this message is a routing table message and it
537
 * contains the default route, then either:
538
 *   i) default_gateway is updated with the address of the gateway.
539
 *   ii) if_index is updated with the interface index for the default route.
540
 * @param if_index[out] possibly updated with interface index for the default
541
 *   route.
542
 * @param default_gateway[out] possibly updated with the default gateway.
543
 * @param nl_hdr the netlink message.
544
 */
545
void MessageHandler(int32_t *if_index,
9✔
546
                    IPV4Address *default_gateway,
547
                    const struct nlmsghdr *nl_hdr) {
548
  // Unless RTA_DST is provided, an RTA_GATEWAY or RTA_OIF attribute implies
549
  // it's the default route.
550
  IPV4Address gateway;
9✔
551
  int32_t index = Interface::DEFAULT_INDEX;
9✔
552

553
  bool is_default_route = true;
9✔
554

555
  // Loop over the attributes looking for RTA_GATEWAY and/or RTA_DST
556
  const rtmsg *rt_msg = reinterpret_cast<const rtmsg*>(NLMSG_DATA(nl_hdr));
9✔
557
  if (rt_msg->rtm_family == AF_INET && rt_msg->rtm_table == RT_TABLE_MAIN) {
9✔
558
    int rt_len = RTM_PAYLOAD(nl_hdr);
2✔
559

560
    for (const rtattr* rt_attr = reinterpret_cast<const rtattr*>(
2✔
561
            RTM_RTA(rt_msg));
562
         RTA_OK(rt_attr, rt_len);
9✔
563
         rt_attr = RTA_NEXT(rt_attr, rt_len)) {
7✔
564
      switch (rt_attr->rta_type) {
7✔
565
        case RTA_OIF:
2✔
566
          index = *(reinterpret_cast<int32_t*>(RTA_DATA(rt_attr)));
2✔
567
          break;
2✔
568
        case RTA_GATEWAY:
1✔
569
          gateway = IPV4Address(
1✔
570
              reinterpret_cast<const in_addr*>(RTA_DATA(rt_attr))->s_addr);
1✔
571
          break;
1✔
572
        case RTA_DST:
1✔
573
          IPV4Address dest(
1✔
574
              reinterpret_cast<const in_addr*>(RTA_DATA(rt_attr))->s_addr);
1✔
575
          is_default_route = dest.IsWildcard();
1✔
576
          break;
577
      }
578
    }
579
  }
580

581
  if (is_default_route &&
10✔
582
      (!gateway.IsWildcard() || index != Interface::DEFAULT_INDEX)) {
15✔
583
    *default_gateway = gateway;
1✔
584
    *if_index = index;
1✔
585
  }
586
}
9✔
587

588
typedef ola::Callback1<void, const struct nlmsghdr*> NetlinkCallback;
589

590
/**
591
 * Read a message from the netlink socket. This continues to read until the
592
 * expected sequence number is seend. Returns true if the desired message was
593
 * seen, false if there was an error reading from the netlink socket.
594
 */
595
bool ReadNetlinkSocket(int sd, uint8_t *buffer, int bufsize, unsigned int seq,
1✔
596
                       NetlinkCallback *handler) {
597
  OLA_DEBUG << "Looking for netlink response with seq: " << seq;
1✔
598
  while (true) {
3✔
599
    int len = recv(sd, buffer, bufsize, 0);
3✔
600
    if (len < 0) {
3✔
601
      return false;
602
    }
603
    if (len == static_cast<int>(bufsize)) {
3✔
UNCOV
604
      OLA_WARN << "Number of bytes fetched == buffer size ("
×
UNCOV
605
               << bufsize << "), Netlink data may be truncated";
×
606
    }
607

608
    struct nlmsghdr* nl_hdr;
609
    for (nl_hdr = reinterpret_cast<struct nlmsghdr*>(buffer);
8✔
610
         NLMSG_OK(nl_hdr, static_cast<unsigned int>(len));
11✔
611
         nl_hdr = NLMSG_NEXT(nl_hdr, len)) {
8✔
612
      OLA_DEBUG << "Read seq " << nl_hdr->nlmsg_seq << ", pid "
9✔
UNCOV
613
                << nl_hdr->nlmsg_pid << ", type "
×
UNCOV
614
                << nl_hdr->nlmsg_type << ", from netlink socket";
×
615

616
      if (static_cast<unsigned int>(nl_hdr->nlmsg_seq) != seq) {
9✔
UNCOV
617
        continue;
×
618
      }
619

620
      if (nl_hdr->nlmsg_type == NLMSG_ERROR) {
9✔
UNCOV
621
        struct nlmsgerr* err = reinterpret_cast<struct nlmsgerr*>(
×
622
            NLMSG_DATA(nl_hdr));
UNCOV
623
        OLA_WARN << "Netlink returned error: " << err->error;
×
UNCOV
624
        return false;
×
625
      }
626

627
      handler->Run(nl_hdr);
9✔
628
      if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0 ||
9✔
629
          nl_hdr->nlmsg_type == NLMSG_DONE) {
9✔
630
        return true;
631
      }
632
    }
633
  }
634
}
635

636
/**
637
 * Get the default route using a netlink socket
638
 */
639
static bool GetDefaultRouteWithNetlink(int32_t *if_index,
1✔
640
                                       IPV4Address *default_gateway) {
641
  int sd = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE);
1✔
642
  if (sd < 0) {
1✔
UNCOV
643
    OLA_WARN << "Could not create Netlink socket " << strerror(errno);
×
UNCOV
644
    return false;
×
645
  }
646
  SocketCloser closer(sd);
1✔
647

648
  int seq = ola::math::Random(0, INT_MAX);
1✔
649

650
  const unsigned int BUFSIZE = 8192;
1✔
651
  uint8_t msg[BUFSIZE];
1✔
652
  memset(msg, 0, BUFSIZE);
1✔
653

654
  nlmsghdr* nl_msg = reinterpret_cast<nlmsghdr*>(msg);
1✔
655
  nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
1✔
656
  nl_msg->nlmsg_type = RTM_GETROUTE;
1✔
657
  nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
1✔
658
  nl_msg->nlmsg_seq = seq++;
1✔
659
  nl_msg->nlmsg_pid = 0;
1✔
660

661
  if (send(sd, nl_msg, nl_msg->nlmsg_len, 0) < 0) {
1✔
UNCOV
662
    OLA_WARN << "Could not send data to Netlink " << strerror(errno);
×
UNCOV
663
    return false;
×
664
  }
665

666
  std::auto_ptr<NetlinkCallback> cb(
1✔
667
      ola::NewCallback(MessageHandler, if_index, default_gateway));
1✔
668
  if (!ReadNetlinkSocket(sd, msg, BUFSIZE, nl_msg->nlmsg_seq, cb.get())) {
1✔
669
    return false;
670
  }
671

672
  if (default_gateway->IsWildcard() && *if_index == Interface::DEFAULT_INDEX) {
1✔
UNCOV
673
    OLA_WARN << "No default route found";
×
674
  }
675
  OLA_INFO << "Default gateway: " << *default_gateway << ", if_index: "
2✔
676
           << *if_index;
1✔
677
  return true;
1✔
678
}
1✔
679
#endif  // USE_SYSCTL_FOR_DEFAULT_ROUTE
680

681
bool DefaultRoute(int32_t *if_index, IPV4Address *default_gateway) {
1✔
682
  *default_gateway = IPV4Address();
1✔
683
  *if_index = Interface::DEFAULT_INDEX;
1✔
684
#ifdef USE_SYSCTL_FOR_DEFAULT_ROUTE
685
  return GetDefaultRouteWithSysctl(if_index, default_gateway);
686
#elif defined(USE_NETLINK_FOR_DEFAULT_ROUTE)
687
  return GetDefaultRouteWithNetlink(if_index, default_gateway);
1✔
688
#elif defined(_WIN32)
689
  ULONG size = 4096;
690
  PMIB_IPFORWARDTABLE forward_table =
691
      reinterpret_cast<PMIB_IPFORWARDTABLE>(malloc(size));
692
  DWORD result = GetIpForwardTable(forward_table, &size, TRUE);
693
  if (result == NO_ERROR) {
694
    for (unsigned int i = 0; i < forward_table->dwNumEntries; ++i) {
695
      if (forward_table->table[i].dwForwardDest == 0) {
696
        *default_gateway =
697
            IPV4Address(forward_table->table[i].dwForwardNextHop);
698
        *if_index = forward_table->table[i].dwForwardIfIndex;
699
      }
700
    }
701
    free(forward_table);
702
    return true;
703
  } else {
704
    OLA_WARN << "GetIpForwardTable failed with " << GetLastError();
705
    return false;
706
  }
707
#else
708
#error "DefaultRoute not implemented for this platform, please report this."
709
  // TODO(Peter): Do something else on machines without Netlink
710
  // No Netlink, can't do anything
711
  return false;
712
#endif  // USE_SYSCTL_FOR_DEFAULT_ROUTE
713
}
714
}  // namespace network
715
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc