]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/iputils.cc
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / iputils.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "iputils.hh"
27
28 #include <fstream>
29 #include <sys/socket.h>
30 #include <boost/format.hpp>
31
32 #ifdef HAVE_GETIFADDRS
33 #include <ifaddrs.h>
34 #endif
35
36 /** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
37
38 static void RuntimeError(const std::string& error)
39 {
40 throw runtime_error(error);
41 }
42
43 static void NetworkErr(const std::string& error)
44 {
45 throw NetworkError(error);
46 }
47
48 int SSocket(int family, int type, int flags)
49 {
50 int ret = socket(family, type, flags);
51 if (ret < 0) {
52 RuntimeError("creating socket of type " + std::to_string(family) + ": " + stringerror());
53 }
54 return ret;
55 }
56
57 int SConnect(int sockfd, const ComboAddress& remote)
58 {
59 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
60 if (ret < 0) {
61 int savederrno = errno;
62 RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
63 }
64 return ret;
65 }
66
67 int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout)
68 {
69 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
70 if(ret < 0) {
71 int savederrno = errno;
72 if (savederrno == EINPROGRESS) {
73 if (timeout <= timeval{0,0}) {
74 return savederrno;
75 }
76
77 /* we wait until the connection has been established */
78 bool error = false;
79 bool disconnected = false;
80 int res = waitForRWData(sockfd, false, timeout.tv_sec, timeout.tv_usec, &error, &disconnected);
81 if (res == 1) {
82 if (error) {
83 savederrno = 0;
84 socklen_t errlen = sizeof(savederrno);
85 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&savederrno, &errlen) == 0) {
86 NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
87 }
88 else {
89 NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
90 }
91 }
92 if (disconnected) {
93 NetworkErr(remote.toStringWithPort() + " closed the connection");
94 }
95 return 0;
96 }
97 else if (res == 0) {
98 NetworkErr("timeout while connecting to " + remote.toStringWithPort());
99 } else if (res < 0) {
100 savederrno = errno;
101 NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
102 }
103 }
104 else {
105 NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
106 }
107 }
108
109 return 0;
110 }
111
112 int SBind(int sockfd, const ComboAddress& local)
113 {
114 int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen());
115 if (ret < 0) {
116 int savederrno = errno;
117 RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
118 }
119 return ret;
120 }
121
122 int SAccept(int sockfd, ComboAddress& remote)
123 {
124 socklen_t remlen = remote.getSocklen();
125
126 int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen);
127 if (ret < 0) {
128 RuntimeError("accepting new connection on socket: " + stringerror());
129 }
130 return ret;
131 }
132
133 int SListen(int sockfd, int limit)
134 {
135 int ret = listen(sockfd, limit);
136 if (ret < 0) {
137 RuntimeError("setting socket to listen: " + stringerror());
138 }
139 return ret;
140 }
141
142 int SSetsockopt(int sockfd, int level, int opname, int value)
143 {
144 int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
145 if (ret < 0) {
146 RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
147 }
148 return ret;
149 }
150
151 void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
152 {
153 if (family == AF_INET) {
154 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
155 #ifdef IP_PMTUDISC_OMIT
156 /* Linux 3.15+ has IP_PMTUDISC_OMIT, which discards PMTU information to prevent
157 poisoning, but still allows fragmentation if the packet size exceeds the
158 outgoing interface MTU, which is good.
159 */
160 try {
161 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
162 return;
163 }
164 catch(const std::exception& e) {
165 /* failed, let's try IP_PMTUDISC_DONT instead */
166 }
167 #endif /* IP_PMTUDISC_OMIT */
168
169 /* IP_PMTUDISC_DONT disables Path MTU discovery */
170 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
171 #endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
172 }
173 else {
174 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
175 #ifdef IPV6_PMTUDISC_OMIT
176 /* Linux 3.15+ has IPV6_PMTUDISC_OMIT, which discards PMTU information to prevent
177 poisoning, but still allows fragmentation if the packet size exceeds the
178 outgoing interface MTU, which is good.
179 */
180 try {
181 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
182 return;
183 }
184 catch(const std::exception& e) {
185 /* failed, let's try IP_PMTUDISC_DONT instead */
186 }
187 #endif /* IPV6_PMTUDISC_OMIT */
188
189 /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
190 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
191 #endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
192 }
193 }
194
195 void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
196 {
197 if (family == AF_INET) {
198 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
199 /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
200 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
201 #elif defined(IP_DONTFRAG)
202 /* at least this prevents fragmentation */
203 SSetsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, 1);
204 #endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) */
205 }
206 else {
207 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
208 /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
209 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
210 #elif defined(IPV6_DONTFRAG)
211 /* at least this prevents fragmentation */
212 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, 1);
213 #endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) */
214 }
215 }
216
217 bool setReusePort(int sockfd)
218 {
219 #if defined(SO_REUSEPORT_LB)
220 try {
221 SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
222 return true;
223 }
224 catch (const std::exception& e) {
225 return false;
226 }
227 #elif defined(SO_REUSEPORT)
228 try {
229 SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
230 return true;
231 }
232 catch (const std::exception& e) {
233 return false;
234 }
235 #endif
236 return false;
237 }
238
239 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
240 {
241 #ifdef SO_TIMESTAMP
242 struct cmsghdr *cmsg;
243 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
244 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) &&
245 CMSG_LEN(sizeof(*tv)) == cmsg->cmsg_len) {
246 memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv));
247 return true;
248 }
249 }
250 #endif
251 return false;
252 }
253 bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
254 {
255 destination->reset();
256 #ifdef __NetBSD__
257 struct cmsghdr* cmsg;
258 #else
259 const struct cmsghdr* cmsg;
260 #endif
261 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
262 #if defined(IP_PKTINFO)
263 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
264 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
265 destination->sin4.sin_addr = i->ipi_addr;
266 destination->sin4.sin_family = AF_INET;
267 return true;
268 }
269 #elif defined(IP_RECVDSTADDR)
270 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
271 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
272 destination->sin4.sin_addr = *i;
273 destination->sin4.sin_family = AF_INET;
274 return true;
275 }
276 #endif
277
278 if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
279 struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
280 destination->sin6.sin6_addr = i->ipi6_addr;
281 destination->sin4.sin_family = AF_INET6;
282 return true;
283 }
284 }
285 return false;
286 }
287
288 bool IsAnyAddress(const ComboAddress& addr)
289 {
290 if(addr.sin4.sin_family == AF_INET)
291 return addr.sin4.sin_addr.s_addr == 0;
292 else if(addr.sin4.sin_family == AF_INET6)
293 return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
294
295 return false;
296 }
297 int sendOnNBSocket(int fd, const struct msghdr *msgh)
298 {
299 int sendErr = 0;
300 #ifdef __OpenBSD__
301 // OpenBSD can and does return EAGAIN on non-blocking datagram sockets
302 for (int i = 0; i < 10; i++) { // Arbitrary upper bound
303 if (sendmsg(fd, msgh, 0) != -1) {
304 sendErr = 0;
305 break;
306 }
307 sendErr = errno;
308 if (sendErr != EAGAIN) {
309 break;
310 }
311 }
312 #else
313 if (sendmsg(fd, msgh, 0) == -1) {
314 sendErr = errno;
315 }
316 #endif
317 return sendErr;
318 }
319
320 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
321 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
322 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
323 void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
324 {
325 iov->iov_base = data;
326 iov->iov_len = datalen;
327
328 memset(msgh, 0, sizeof(struct msghdr));
329
330 msgh->msg_control = cbuf;
331 msgh->msg_controllen = cbufsize;
332 msgh->msg_name = addr;
333 msgh->msg_namelen = addr->getSocklen();
334 msgh->msg_iov = iov;
335 msgh->msg_iovlen = 1;
336 msgh->msg_flags = 0;
337 }
338
339 // warning: various parts of PowerDNS assume 'truncate' will never throw
340 void ComboAddress::truncate(unsigned int bits) noexcept
341 {
342 uint8_t* start;
343 int len=4;
344 if(sin4.sin_family==AF_INET) {
345 if(bits >= 32)
346 return;
347 start = (uint8_t*)&sin4.sin_addr.s_addr;
348 len=4;
349 }
350 else {
351 if(bits >= 128)
352 return;
353 start = (uint8_t*)&sin6.sin6_addr.s6_addr;
354 len=16;
355 }
356
357 auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should
358
359 memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right
360
361 auto bitsleft=tozero % 8; // 2 bits left to clear
362
363 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
364 // so and by '11111100', which is ~((1<<2)-1) = ~3
365 uint8_t* place = start + len - 1 - tozero/8;
366 *place &= (~((1<<bitsleft)-1));
367 }
368
369 size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
370 {
371 struct msghdr msgh;
372 struct iovec iov;
373 cmsgbuf_aligned cbuf;
374
375 /* Set up iov and msgh structures. */
376 memset(&msgh, 0, sizeof(struct msghdr));
377 msgh.msg_control = nullptr;
378 msgh.msg_controllen = 0;
379 if (dest) {
380 msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
381 msgh.msg_namelen = dest->getSocklen();
382 }
383 else {
384 msgh.msg_name = nullptr;
385 msgh.msg_namelen = 0;
386 }
387
388 msgh.msg_flags = 0;
389
390 if (localItf != 0 && local) {
391 addCMsgSrcAddr(&msgh, &cbuf, local, localItf);
392 }
393
394 iov.iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
395 iov.iov_len = len;
396 msgh.msg_iov = &iov;
397 msgh.msg_iovlen = 1;
398 msgh.msg_flags = 0;
399
400 size_t sent = 0;
401 #ifdef MSG_FASTOPEN
402 bool firstTry = true;
403 #endif
404
405 do {
406
407 #ifdef MSG_FASTOPEN
408 if (flags & MSG_FASTOPEN && firstTry == false) {
409 flags &= ~MSG_FASTOPEN;
410 }
411 #endif /* MSG_FASTOPEN */
412
413 ssize_t res = sendmsg(fd, &msgh, flags);
414
415 if (res > 0) {
416 size_t written = static_cast<size_t>(res);
417 sent += written;
418
419 if (sent == len) {
420 return sent;
421 }
422
423 /* partial write */
424 #ifdef MSG_FASTOPEN
425 firstTry = false;
426 #endif
427 iov.iov_len -= written;
428 iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
429 }
430 else if (res == 0) {
431 return res;
432 }
433 else if (res == -1) {
434 int err = errno;
435 if (err == EINTR) {
436 continue;
437 }
438 else if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
439 /* EINPROGRESS might happen with non blocking socket,
440 especially with TCP Fast Open */
441 return sent;
442 }
443 else {
444 unixDie("failed in sendMsgWithTimeout");
445 }
446 }
447 }
448 while (true);
449
450 return 0;
451 }
452
453 template class NetmaskTree<bool, Netmask>;
454
455 /* requires a non-blocking socket.
456 On Linux, we could use MSG_DONTWAIT on a blocking socket
457 but this is not portable.
458 */
459 bool isTCPSocketUsable(int sock)
460 {
461 int err = 0;
462 char buf = '\0';
463 size_t buf_size = sizeof(buf);
464
465 do {
466 ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
467
468 if (got > 0) {
469 /* socket is usable, some data is even waiting to be read */
470 return true;
471 }
472 else if (got == 0) {
473 /* other end has closed the socket */
474 return false;
475 }
476 else {
477 err = errno;
478
479 if (err == EAGAIN || err == EWOULDBLOCK) {
480 /* socket is usable, no data waiting */
481 return true;
482 }
483 else {
484 if (err != EINTR) {
485 /* something is wrong, could be ECONNRESET,
486 ENOTCONN, EPIPE, but anyway this socket is
487 not usable. */
488 return false;
489 }
490 }
491 }
492 } while (err == EINTR);
493
494 return false;
495 }
496 /* mission in life: parse four cases
497 1) [2002::1]:53
498 2) 1.2.3.4
499 3) 1.2.3.4:5300
500 4) 2001::1 no port allowed
501 */
502
503 ComboAddress parseIPAndPort(const std::string& input, uint16_t port)
504 {
505 if (input[0] == '[') { // case 1
506 auto both = splitField(input.substr(1), ']');
507 return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
508 }
509
510 string::size_type count = 0;
511 for (char c : input) {
512 if (c == ':') {
513 count++;
514 }
515 if (count > 1) {
516 break;
517 }
518 }
519 switch (count) {
520 case 0: // case 2
521 return ComboAddress(input, port);
522 case 1: { // case 3
523 string::size_type cpos = input.rfind(':');
524 pair<std::string,std::string> both;
525 both.first = input.substr(0, cpos);
526 both.second = input.substr(cpos + 1);
527
528 auto newport = pdns::checked_stoi<uint16_t>(both.second);
529 return ComboAddress(both.first, newport);
530 }
531 default: // case 4
532 return ComboAddress(input, port);
533 }
534 }
535
536 void setSocketBuffer(int fd, int optname, uint32_t size)
537 {
538 uint32_t psize = 0;
539 socklen_t len = sizeof(psize);
540
541 if (getsockopt(fd, SOL_SOCKET, optname, &psize, &len) != 0) {
542 throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
543 }
544 if (psize >= size) {
545 return;
546 }
547 if (setsockopt(fd, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
548 throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
549 }
550 }
551
552 void setSocketReceiveBuffer(int fd, uint32_t size)
553 {
554 setSocketBuffer(fd, SO_RCVBUF, size);
555 }
556
557 void setSocketSendBuffer(int fd, uint32_t size)
558 {
559 setSocketBuffer(fd, SO_SNDBUF, size);
560 }
561
562 #ifdef __linux__
563 static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
564 {
565 std::ifstream ifs(readMaxFromFile);
566 if (ifs) {
567 std::string line;
568 if (getline(ifs, line)) {
569 auto max = pdns::checked_stoi<uint32_t>(line);
570 setSocketBuffer(socket, optname, max);
571 return max;
572 }
573 }
574 return 0;
575 }
576 #endif
577
578 uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
579 {
580 #ifdef __linux__
581 return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
582 #else
583 return 0;
584 #endif
585 }
586
587 uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
588 {
589 #ifdef __linux__
590 return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
591 #else
592 return 0;
593 #endif
594 }
595
596 std::set<std::string> getListOfNetworkInterfaces()
597 {
598 std::set<std::string> result;
599 #ifdef HAVE_GETIFADDRS
600 struct ifaddrs *ifaddr;
601 if (getifaddrs(&ifaddr) == -1) {
602 return result;
603 }
604
605 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
606 if (ifa->ifa_name == nullptr) {
607 continue;
608 }
609 result.insert(ifa->ifa_name);
610 }
611
612 freeifaddrs(ifaddr);
613 #endif
614 return result;
615 }
616
617 #ifdef HAVE_GETIFADDRS
618 std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
619 {
620 std::vector<ComboAddress> result;
621 struct ifaddrs *ifaddr = nullptr;
622 if (getifaddrs(&ifaddr) == -1) {
623 return result;
624 }
625
626 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
627 if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
628 continue;
629 }
630 if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
631 continue;
632 }
633 ComboAddress addr;
634 try {
635 addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
636 }
637 catch (...) {
638 continue;
639 }
640
641 result.push_back(addr);
642 }
643
644 freeifaddrs(ifaddr);
645 return result;
646 }
647 #else
648 std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& /* itf */)
649 {
650 std::vector<ComboAddress> result;
651 return result;
652 }
653 #endif // HAVE_GETIFADDRS
654
655 #ifdef HAVE_GETIFADDRS
656 static uint8_t convertNetmaskToBits(const uint8_t* mask, socklen_t len)
657 {
658 if (mask == nullptr || len > 16) {
659 throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
660 }
661
662 uint8_t result = 0;
663 // for all bytes in the address (4 for IPv4, 16 for IPv6)
664 for (size_t idx = 0; idx < len; idx++) {
665 uint8_t byte = *(mask + idx);
666 // count the number of bits set
667 while (byte > 0) {
668 result += (byte & 1);
669 byte >>= 1;
670 }
671 }
672 return result;
673 }
674 #endif /* HAVE_GETIFADDRS */
675
676 #ifdef HAVE_GETIFADDRS
677 std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
678 {
679 std::vector<Netmask> result;
680 struct ifaddrs *ifaddr = nullptr;
681 if (getifaddrs(&ifaddr) == -1) {
682 return result;
683 }
684
685 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
686 if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
687 continue;
688 }
689 if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
690 continue;
691 }
692 ComboAddress addr;
693 try {
694 addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
695 }
696 catch (...) {
697 continue;
698 }
699
700 if (ifa->ifa_addr->sa_family == AF_INET) {
701 auto netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
702 uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
703 result.emplace_back(addr, maskBits);
704 }
705 else if (ifa->ifa_addr->sa_family == AF_INET6) {
706 auto netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
707 uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
708 result.emplace_back(addr, maskBits);
709 }
710 }
711
712 freeifaddrs(ifaddr);
713 return result;
714 }
715 #else
716 std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& /* itf */)
717 {
718 std::vector<Netmask> result;
719 return result;
720 }
721 #endif // HAVE_GETIFADDRS