1/* $NetBSD: net.c,v 1.1 2024/02/18 20:57:57 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16#include <stdbool.h> 17#include <sys/types.h> 18 19#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) 20#if defined(HAVE_SYS_PARAM_H) 21#include <sys/param.h> 22#endif /* if defined(HAVE_SYS_PARAM_H) */ 23#include <sys/sysctl.h> 24#endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */ 25#include <errno.h> 26#include <fcntl.h> 27#include <sys/uio.h> 28#include <unistd.h> 29 30#include <isc/log.h> 31#include <isc/net.h> 32#include <isc/netdb.h> 33#include <isc/once.h> 34#include <isc/strerr.h> 35#include <isc/string.h> 36#include <isc/util.h> 37 38#ifndef socklen_t 39#define socklen_t unsigned int 40#endif /* ifndef socklen_t */ 41 42/*% 43 * Definitions about UDP port range specification. This is a total mess of 44 * portability variants: some use sysctl (but the sysctl names vary), some use 45 * system-specific interfaces, some have the same interface for IPv4 and IPv6, 46 * some separate them, etc... 47 */ 48 49/*% 50 * The last resort defaults: use all non well known port space 51 */ 52#ifndef ISC_NET_PORTRANGELOW 53#define ISC_NET_PORTRANGELOW 1024 54#endif /* ISC_NET_PORTRANGELOW */ 55#ifndef ISC_NET_PORTRANGEHIGH 56#define ISC_NET_PORTRANGEHIGH 65535 57#endif /* ISC_NET_PORTRANGEHIGH */ 58 59#ifdef HAVE_SYSCTLBYNAME 60 61/*% 62 * sysctl variants 63 */ 64#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) 65#define USE_SYSCTL_PORTRANGE 66#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.portrange.hifirst" 67#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast" 68#define SYSCTL_V6PORTRANGE_LOW "net.inet.ip.portrange.hifirst" 69#define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast" 70#endif /* if defined(__FreeBSD__) || defined(__APPLE__) || \ 71 * defined(__DragonFly__) */ 72 73#ifdef __NetBSD__ 74#define USE_SYSCTL_PORTRANGE 75#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.anonportmin" 76#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax" 77#define SYSCTL_V6PORTRANGE_LOW "net.inet6.ip6.anonportmin" 78#define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax" 79#endif /* ifdef __NetBSD__ */ 80 81#else /* !HAVE_SYSCTLBYNAME */ 82 83#ifdef __OpenBSD__ 84#define USE_SYSCTL_PORTRANGE 85#define SYSCTL_V4PORTRANGE_LOW \ 86 { \ 87 CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HIFIRSTAUTO \ 88 } 89#define SYSCTL_V4PORTRANGE_HIGH \ 90 { \ 91 CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HILASTAUTO \ 92 } 93/* Same for IPv6 */ 94#define SYSCTL_V6PORTRANGE_LOW SYSCTL_V4PORTRANGE_LOW 95#define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH 96#endif /* ifdef __OpenBSD__ */ 97 98#endif /* HAVE_SYSCTLBYNAME */ 99 100static isc_once_t once_ipv6only = ISC_ONCE_INIT; 101#ifdef __notyet__ 102static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 103#endif /* ifdef __notyet__ */ 104 105#ifndef ISC_CMSG_IP_TOS 106#ifdef __APPLE__ 107#define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */ 108#else /* ! __APPLE__ */ 109#define ISC_CMSG_IP_TOS 1 110#endif /* ! __APPLE__ */ 111#endif /* ! ISC_CMSG_IP_TOS */ 112 113static isc_once_t once = ISC_ONCE_INIT; 114static isc_once_t once_dscp = ISC_ONCE_INIT; 115 116static isc_result_t ipv4_result = ISC_R_NOTFOUND; 117static isc_result_t ipv6_result = ISC_R_NOTFOUND; 118static isc_result_t unix_result = ISC_R_NOTFOUND; 119static isc_result_t ipv6only_result = ISC_R_NOTFOUND; 120static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 121static unsigned int dscp_result = 0; 122 123static isc_result_t 124try_proto(int domain) { 125 int s; 126 isc_result_t result = ISC_R_SUCCESS; 127 char strbuf[ISC_STRERRORSIZE]; 128 129 s = socket(domain, SOCK_STREAM, 0); 130 if (s == -1) { 131 switch (errno) { 132#ifdef EAFNOSUPPORT 133 case EAFNOSUPPORT: 134#endif /* ifdef EAFNOSUPPORT */ 135#ifdef EPFNOSUPPORT 136 case EPFNOSUPPORT: 137#endif /* ifdef EPFNOSUPPORT */ 138#ifdef EPROTONOSUPPORT 139 case EPROTONOSUPPORT: 140#endif /* ifdef EPROTONOSUPPORT */ 141#ifdef EINVAL 142 case EINVAL: 143#endif /* ifdef EINVAL */ 144 return (ISC_R_NOTFOUND); 145 default: 146 strerror_r(errno, strbuf, sizeof(strbuf)); 147 UNEXPECTED_ERROR(__FILE__, __LINE__, 148 "socket() failed: %s", strbuf); 149 return (ISC_R_UNEXPECTED); 150 } 151 } 152 153 if (domain == PF_INET6) { 154 struct sockaddr_in6 sin6; 155 unsigned int len; 156 157 /* 158 * Check to see if IPv6 is broken, as is common on Linux. 159 */ 160 len = sizeof(sin6); 161 if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0) 162 { 163 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 164 ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, 165 "retrieving the address of an IPv6 " 166 "socket from the kernel failed."); 167 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 168 ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, 169 "IPv6 is not supported."); 170 result = ISC_R_NOTFOUND; 171 } else { 172 if (len == sizeof(struct sockaddr_in6)) { 173 result = ISC_R_SUCCESS; 174 } else { 175 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 176 ISC_LOGMODULE_SOCKET, 177 ISC_LOG_ERROR, 178 "IPv6 structures in kernel and " 179 "user space do not match."); 180 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 181 ISC_LOGMODULE_SOCKET, 182 ISC_LOG_ERROR, 183 "IPv6 is not supported."); 184 result = ISC_R_NOTFOUND; 185 } 186 } 187 } 188 189 (void)close(s); 190 191 return (result); 192} 193 194static void 195initialize_action(void) { 196 ipv4_result = try_proto(PF_INET); 197 ipv6_result = try_proto(PF_INET6); 198#ifdef ISC_PLATFORM_HAVESYSUNH 199 unix_result = try_proto(PF_UNIX); 200#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */ 201} 202 203static void 204initialize(void) { 205 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); 206} 207 208isc_result_t 209isc_net_probeipv4(void) { 210 initialize(); 211 return (ipv4_result); 212} 213 214isc_result_t 215isc_net_probeipv6(void) { 216 initialize(); 217 return (ipv6_result); 218} 219 220isc_result_t 221isc_net_probeunix(void) { 222 initialize(); 223 return (unix_result); 224} 225 226static void 227try_ipv6only(void) { 228#ifdef IPV6_V6ONLY 229 int s, on; 230 char strbuf[ISC_STRERRORSIZE]; 231#endif /* ifdef IPV6_V6ONLY */ 232 isc_result_t result; 233 234 result = isc_net_probeipv6(); 235 if (result != ISC_R_SUCCESS) { 236 ipv6only_result = result; 237 return; 238 } 239 240#ifndef IPV6_V6ONLY 241 ipv6only_result = ISC_R_NOTFOUND; 242 return; 243#else /* ifndef IPV6_V6ONLY */ 244 /* check for TCP sockets */ 245 s = socket(PF_INET6, SOCK_STREAM, 0); 246 if (s == -1) { 247 strerror_r(errno, strbuf, sizeof(strbuf)); 248 UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", 249 strbuf); 250 ipv6only_result = ISC_R_UNEXPECTED; 251 return; 252 } 253 254 on = 1; 255 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { 256 ipv6only_result = ISC_R_NOTFOUND; 257 goto close; 258 } 259 260 close(s); 261 262 /* check for UDP sockets */ 263 s = socket(PF_INET6, SOCK_DGRAM, 0); 264 if (s == -1) { 265 strerror_r(errno, strbuf, sizeof(strbuf)); 266 UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", 267 strbuf); 268 ipv6only_result = ISC_R_UNEXPECTED; 269 return; 270 } 271 272 on = 1; 273 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { 274 ipv6only_result = ISC_R_NOTFOUND; 275 goto close; 276 } 277 278 ipv6only_result = ISC_R_SUCCESS; 279 280close: 281 close(s); 282 return; 283#endif /* IPV6_V6ONLY */ 284} 285 286static void 287initialize_ipv6only(void) { 288 RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) == 289 ISC_R_SUCCESS); 290} 291 292#ifdef __notyet__ 293static void 294try_ipv6pktinfo(void) { 295 int s, on; 296 char strbuf[ISC_STRERRORSIZE]; 297 isc_result_t result; 298 int optname; 299 300 result = isc_net_probeipv6(); 301 if (result != ISC_R_SUCCESS) { 302 ipv6pktinfo_result = result; 303 return; 304 } 305 306 /* we only use this for UDP sockets */ 307 s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 308 if (s == -1) { 309 strerror_r(errno, strbuf, sizeof(strbuf)); 310 UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", 311 strbuf); 312 ipv6pktinfo_result = ISC_R_UNEXPECTED; 313 return; 314 } 315 316#ifdef IPV6_RECVPKTINFO 317 optname = IPV6_RECVPKTINFO; 318#else /* ifdef IPV6_RECVPKTINFO */ 319 optname = IPV6_PKTINFO; 320#endif /* ifdef IPV6_RECVPKTINFO */ 321 on = 1; 322 if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { 323 ipv6pktinfo_result = ISC_R_NOTFOUND; 324 goto close; 325 } 326 327 ipv6pktinfo_result = ISC_R_SUCCESS; 328 329close: 330 close(s); 331 return; 332} 333 334static void 335initialize_ipv6pktinfo(void) { 336 RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo) == 337 ISC_R_SUCCESS); 338} 339#endif /* ifdef __notyet__ */ 340 341isc_result_t 342isc_net_probe_ipv6only(void) { 343 initialize_ipv6only(); 344 return (ipv6only_result); 345} 346 347isc_result_t 348isc_net_probe_ipv6pktinfo(void) { 349/* 350 * XXXWPK if pktinfo were supported then we could listen on :: for ipv6 and get 351 * the information about the destination address from pktinfo structure passed 352 * in recvmsg but this method is not portable and libuv doesn't support it - so 353 * we need to listen on all interfaces. 354 * We should verify that this doesn't impact performance (we already do it for 355 * ipv4) and either remove all the ipv6pktinfo detection code from above 356 * or think of fixing libuv. 357 */ 358#ifdef __notyet__ 359 initialize_ipv6pktinfo(); 360#endif /* ifdef __notyet__ */ 361 return (ipv6pktinfo_result); 362} 363 364#if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS) 365 366static socklen_t 367cmsg_len(socklen_t len) { 368#ifdef CMSG_LEN 369 return (CMSG_LEN(len)); 370#else /* ifdef CMSG_LEN */ 371 socklen_t hdrlen; 372 373 /* 374 * Cast NULL so that any pointer arithmetic performed by CMSG_DATA 375 * is correct. 376 */ 377 hdrlen = (socklen_t)CMSG_DATA(((struct cmsghdr *)NULL)); 378 return (hdrlen + len); 379#endif /* ifdef CMSG_LEN */ 380} 381 382static socklen_t 383cmsg_space(socklen_t len) { 384#ifdef CMSG_SPACE 385 return (CMSG_SPACE(len)); 386#else /* ifdef CMSG_SPACE */ 387 struct msghdr msg; 388 struct cmsghdr *cmsgp; 389 /* 390 * XXX: The buffer length is an ad-hoc value, but should be enough 391 * in a practical sense. 392 */ 393 char dummybuf[sizeof(struct cmsghdr) + 1024]; 394 395 memset(&msg, 0, sizeof(msg)); 396 msg.msg_control = dummybuf; 397 msg.msg_controllen = sizeof(dummybuf); 398 399 cmsgp = (struct cmsghdr *)dummybuf; 400 cmsgp->cmsg_len = cmsg_len(len); 401 402 cmsgp = CMSG_NXTHDR(&msg, cmsgp); 403 if (cmsgp != NULL) { 404 return ((char *)cmsgp - (char *)msg.msg_control); 405 } else { 406 return (0); 407 } 408#endif /* ifdef CMSG_SPACE */ 409} 410 411/* 412 * Make a fd non-blocking. 413 */ 414static isc_result_t 415make_nonblock(int fd) { 416 int ret; 417 int flags; 418 char strbuf[ISC_STRERRORSIZE]; 419#ifdef USE_FIONBIO_IOCTL 420 int on = 1; 421 422 ret = ioctl(fd, FIONBIO, (char *)&on); 423#else /* ifdef USE_FIONBIO_IOCTL */ 424 flags = fcntl(fd, F_GETFL, 0); 425 flags |= PORT_NONBLOCK; 426 ret = fcntl(fd, F_SETFL, flags); 427#endif /* ifdef USE_FIONBIO_IOCTL */ 428 429 if (ret == -1) { 430 strerror_r(errno, strbuf, sizeof(strbuf)); 431 UNEXPECTED_ERROR(__FILE__, __LINE__, 432#ifdef USE_FIONBIO_IOCTL 433 "ioctl(%d, FIONBIO, &on): %s", fd, 434#else /* ifdef USE_FIONBIO_IOCTL */ 435 "fcntl(%d, F_SETFL, %d): %s", fd, flags, 436#endif /* ifdef USE_FIONBIO_IOCTL */ 437 strbuf); 438 439 return (ISC_R_UNEXPECTED); 440 } 441 442 return (ISC_R_SUCCESS); 443} 444 445static bool 446cmsgsend(int s, int level, int type, struct addrinfo *res) { 447 char strbuf[ISC_STRERRORSIZE]; 448 struct sockaddr_storage ss; 449 socklen_t len = sizeof(ss); 450 struct msghdr msg; 451 union { 452 struct cmsghdr h; 453 unsigned char b[256]; 454 } control; 455 struct cmsghdr *cmsgp; 456 int dscp = (46 << 2); /* Expedited forwarding. */ 457 struct iovec iovec; 458 char buf[1] = { 0 }; 459 isc_result_t result; 460 461 if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { 462 strerror_r(errno, strbuf, sizeof(strbuf)); 463 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 464 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 465 "bind: %s", strbuf); 466 return (false); 467 } 468 469 if (getsockname(s, (struct sockaddr *)&ss, &len) < 0) { 470 strerror_r(errno, strbuf, sizeof(strbuf)); 471 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 472 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 473 "getsockname: %s", strbuf); 474 return (false); 475 } 476 477 iovec.iov_base = buf; 478 iovec.iov_len = sizeof(buf); 479 480 memset(&msg, 0, sizeof(msg)); 481 msg.msg_name = (struct sockaddr *)&ss; 482 msg.msg_namelen = len; 483 msg.msg_iov = &iovec; 484 msg.msg_iovlen = 1; 485 msg.msg_control = (void *)&control; 486 msg.msg_controllen = 0; 487 msg.msg_flags = 0; 488 489 cmsgp = msg.msg_control; 490 491 switch (type) { 492#ifdef IP_TOS 493 case IP_TOS: 494 memset(cmsgp, 0, cmsg_space(sizeof(char))); 495 cmsgp->cmsg_level = level; 496 cmsgp->cmsg_type = type; 497 cmsgp->cmsg_len = cmsg_len(sizeof(char)); 498 *(unsigned char *)CMSG_DATA(cmsgp) = dscp; 499 msg.msg_controllen += cmsg_space(sizeof(char)); 500 break; 501#endif /* ifdef IP_TOS */ 502#ifdef IPV6_TCLASS 503 case IPV6_TCLASS: 504 memset(cmsgp, 0, cmsg_space(sizeof(dscp))); 505 cmsgp->cmsg_level = level; 506 cmsgp->cmsg_type = type; 507 cmsgp->cmsg_len = cmsg_len(sizeof(dscp)); 508 memmove(CMSG_DATA(cmsgp), &dscp, sizeof(dscp)); 509 msg.msg_controllen += cmsg_space(sizeof(dscp)); 510 break; 511#endif /* ifdef IPV6_TCLASS */ 512 default: 513 UNREACHABLE(); 514 } 515 516 if (sendmsg(s, &msg, 0) < 0) { 517 int debug = ISC_LOG_DEBUG(10); 518 const char *typestr; 519 switch (errno) { 520#ifdef ENOPROTOOPT 521 case ENOPROTOOPT: 522#endif /* ifdef ENOPROTOOPT */ 523#ifdef EOPNOTSUPP 524 case EOPNOTSUPP: 525#endif /* ifdef EOPNOTSUPP */ 526 case EINVAL: 527 case EPERM: 528 break; 529 default: 530 debug = ISC_LOG_NOTICE; 531 } 532 strerror_r(errno, strbuf, sizeof(strbuf)); 533 if (debug != ISC_LOG_NOTICE) { 534 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 535 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 536 "sendmsg: %s", strbuf); 537 } else { 538 typestr = (type == IP_TOS) ? "IP_TOS" : "IPV6_TCLASS"; 539 UNEXPECTED_ERROR(__FILE__, __LINE__, 540 "probing " 541 "sendmsg() with %s=%02x failed: %s", 542 typestr, dscp, strbuf); 543 } 544 return (false); 545 } 546 547 /* 548 * Make sure the message actually got sent. 549 */ 550 result = make_nonblock(s); 551 RUNTIME_CHECK(result == ISC_R_SUCCESS); 552 553 iovec.iov_base = buf; 554 iovec.iov_len = sizeof(buf); 555 556 memset(&msg, 0, sizeof(msg)); 557 msg.msg_name = (struct sockaddr *)&ss; 558 msg.msg_namelen = sizeof(ss); 559 msg.msg_iov = &iovec; 560 msg.msg_iovlen = 1; 561 msg.msg_control = NULL; 562 msg.msg_controllen = 0; 563 msg.msg_flags = 0; 564 565 if (recvmsg(s, &msg, 0) < 0) { 566 return (false); 567 } 568 569 return (true); 570} 571#endif /* if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS) */ 572 573static void 574try_dscp_v4(void) { 575#ifdef IP_TOS 576 char strbuf[ISC_STRERRORSIZE]; 577 struct addrinfo hints, *res0; 578 int s, dscp = 0, n; 579#ifdef IP_RECVTOS 580 int on = 1; 581#endif /* IP_RECVTOS */ 582 583 memset(&hints, 0, sizeof(hints)); 584 hints.ai_family = AF_INET; 585 hints.ai_socktype = SOCK_DGRAM; 586 hints.ai_protocol = IPPROTO_UDP; 587#ifdef AI_NUMERICHOST 588 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 589#else /* ifdef AI_NUMERICHOST */ 590 hints.ai_flags = AI_PASSIVE; 591#endif /* ifdef AI_NUMERICHOST */ 592 593 n = getaddrinfo("127.0.0.1", NULL, &hints, &res0); 594 if (n != 0 || res0 == NULL) { 595 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 596 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 597 "getaddrinfo(127.0.0.1): %s", gai_strerror(n)); 598 return; 599 } 600 601 s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); 602 603 if (s == -1) { 604 strerror_r(errno, strbuf, sizeof(strbuf)); 605 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 606 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 607 "socket: %s", strbuf); 608 freeaddrinfo(res0); 609 return; 610 } 611 612 if (setsockopt(s, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) == 0) { 613 dscp_result |= ISC_NET_DSCPSETV4; 614 } 615 616#ifdef IP_RECVTOS 617 on = 1; 618 if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on)) == 0) { 619 dscp_result |= ISC_NET_DSCPRECVV4; 620 } 621#endif /* IP_RECVTOS */ 622 623#if ISC_CMSG_IP_TOS 624 if (cmsgsend(s, IPPROTO_IP, IP_TOS, res0)) { 625 dscp_result |= ISC_NET_DSCPPKTV4; 626 } 627#endif /* ISC_CMSG_IP_TOS */ 628 629 freeaddrinfo(res0); 630 close(s); 631 632#endif /* IP_TOS */ 633} 634 635static void 636try_dscp_v6(void) { 637#ifdef IPV6_TCLASS 638 char strbuf[ISC_STRERRORSIZE]; 639 struct addrinfo hints, *res0; 640 int s, dscp = 0, n; 641#if defined(IPV6_RECVTCLASS) 642 int on = 1; 643#endif /* IPV6_RECVTCLASS */ 644 645 memset(&hints, 0, sizeof(hints)); 646 hints.ai_family = AF_INET6; 647 hints.ai_socktype = SOCK_DGRAM; 648 hints.ai_protocol = IPPROTO_UDP; 649#ifdef AI_NUMERICHOST 650 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 651#else /* ifdef AI_NUMERICHOST */ 652 hints.ai_flags = AI_PASSIVE; 653#endif /* ifdef AI_NUMERICHOST */ 654 655 n = getaddrinfo("::1", NULL, &hints, &res0); 656 if (n != 0 || res0 == NULL) { 657 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 658 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 659 "getaddrinfo(::1): %s", gai_strerror(n)); 660 return; 661 } 662 663 s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); 664 if (s == -1) { 665 strerror_r(errno, strbuf, sizeof(strbuf)); 666 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 667 ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), 668 "socket: %s", strbuf); 669 freeaddrinfo(res0); 670 return; 671 } 672 if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp)) == 0) 673 { 674 dscp_result |= ISC_NET_DSCPSETV6; 675 } 676 677#ifdef IPV6_RECVTCLASS 678 on = 1; 679 if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, sizeof(on)) == 0) 680 { 681 dscp_result |= ISC_NET_DSCPRECVV6; 682 } 683#endif /* IPV6_RECVTCLASS */ 684 685 if (cmsgsend(s, IPPROTO_IPV6, IPV6_TCLASS, res0)) { 686 dscp_result |= ISC_NET_DSCPPKTV6; 687 } 688 689 freeaddrinfo(res0); 690 close(s); 691 692#endif /* IPV6_TCLASS */ 693} 694 695static void 696try_dscp(void) { 697 try_dscp_v4(); 698 try_dscp_v6(); 699} 700 701static void 702initialize_dscp(void) { 703 RUNTIME_CHECK(isc_once_do(&once_dscp, try_dscp) == ISC_R_SUCCESS); 704} 705 706unsigned int 707isc_net_probedscp(void) { 708 initialize_dscp(); 709 return (dscp_result); 710} 711 712#if defined(USE_SYSCTL_PORTRANGE) 713#if defined(HAVE_SYSCTLBYNAME) 714static isc_result_t 715getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { 716 int port_low, port_high; 717 size_t portlen; 718 const char *sysctlname_lowport, *sysctlname_hiport; 719 720 if (af == AF_INET) { 721 sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW; 722 sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH; 723 } else { 724 sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW; 725 sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH; 726 } 727 portlen = sizeof(port_low); 728 if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, NULL, 0) < 0) 729 { 730 return (ISC_R_FAILURE); 731 } 732 portlen = sizeof(port_high); 733 if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, NULL, 0) < 0) 734 { 735 return (ISC_R_FAILURE); 736 } 737 if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) { 738 return (ISC_R_RANGE); 739 } 740 741 *low = (in_port_t)port_low; 742 *high = (in_port_t)port_high; 743 744 return (ISC_R_SUCCESS); 745} 746#else /* !HAVE_SYSCTLBYNAME */ 747static isc_result_t 748getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { 749 int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW; 750 int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH; 751 int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW; 752 int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH; 753 int *mib_lo, *mib_hi, miblen; 754 int port_low, port_high; 755 size_t portlen; 756 757 if (af == AF_INET) { 758 mib_lo = mib_lo4; 759 mib_hi = mib_hi4; 760 miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]); 761 } else { 762 mib_lo = mib_lo6; 763 mib_hi = mib_hi6; 764 miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]); 765 } 766 767 portlen = sizeof(port_low); 768 if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) { 769 return (ISC_R_FAILURE); 770 } 771 772 portlen = sizeof(port_high); 773 if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) { 774 return (ISC_R_FAILURE); 775 } 776 777 if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) { 778 return (ISC_R_RANGE); 779 } 780 781 *low = (in_port_t)port_low; 782 *high = (in_port_t)port_high; 783 784 return (ISC_R_SUCCESS); 785} 786#endif /* HAVE_SYSCTLBYNAME */ 787#endif /* USE_SYSCTL_PORTRANGE */ 788 789isc_result_t 790isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { 791 int result = ISC_R_FAILURE; 792#if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) 793 FILE *fp; 794#endif /* if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) */ 795 796 REQUIRE(low != NULL && high != NULL); 797 798#if defined(USE_SYSCTL_PORTRANGE) 799 result = getudpportrange_sysctl(af, low, high); 800#elif defined(__linux) 801 802 UNUSED(af); 803 804 /* 805 * Linux local ports are address family agnostic. 806 */ 807 fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r"); 808 if (fp != NULL) { 809 int n; 810 unsigned int l, h; 811 812 n = fscanf(fp, "%u %u", &l, &h); 813 if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) { 814 *low = l; 815 *high = h; 816 result = ISC_R_SUCCESS; 817 } 818 fclose(fp); 819 } 820#else /* if defined(USE_SYSCTL_PORTRANGE) */ 821 UNUSED(af); 822#endif /* if defined(USE_SYSCTL_PORTRANGE) */ 823 824 if (result != ISC_R_SUCCESS) { 825 *low = ISC_NET_PORTRANGELOW; 826 *high = ISC_NET_PORTRANGEHIGH; 827 } 828 829 return (ISC_R_SUCCESS); /* we currently never fail in this function */ 830} 831 832void 833isc_net_disableipv4(void) { 834 initialize(); 835 if (ipv4_result == ISC_R_SUCCESS) { 836 ipv4_result = ISC_R_DISABLED; 837 } 838} 839 840void 841isc_net_disableipv6(void) { 842 initialize(); 843 if (ipv6_result == ISC_R_SUCCESS) { 844 ipv6_result = ISC_R_DISABLED; 845 } 846} 847 848void 849isc_net_enableipv4(void) { 850 initialize(); 851 if (ipv4_result == ISC_R_DISABLED) { 852 ipv4_result = ISC_R_SUCCESS; 853 } 854} 855 856void 857isc_net_enableipv6(void) { 858 initialize(); 859 if (ipv6_result == ISC_R_DISABLED) { 860 ipv6_result = ISC_R_SUCCESS; 861 } 862} 863