1/* $NetBSD: inet_addr_local.c,v 1.4 2017/02/14 01:16:49 christos Exp $ */ 2 3/*++ 4/* NAME 5/* inet_addr_local 3 6/* SUMMARY 7/* determine if IP address is local 8/* SYNOPSIS 9/* #include <inet_addr_local.h> 10/* 11/* int inet_addr_local(addr_list, mask_list, addr_family_list) 12/* INET_ADDR_LIST *addr_list; 13/* INET_ADDR_LIST *mask_list; 14/* unsigned *addr_family; 15/* DESCRIPTION 16/* inet_addr_local() determines all active IP interface addresses 17/* of the local system. Any address found is appended to the 18/* specified address list. The result value is the number of 19/* active interfaces found. 20/* 21/* The mask_list is either a null pointer, or it is a list that 22/* receives the netmasks of the interface addresses that were found. 23/* 24/* The addr_family_list specifies one or more of AF_INET or AF_INET6. 25/* DIAGNOSTICS 26/* Fatal errors: out of memory. 27/* SEE ALSO 28/* inet_addr_list(3) address list management 29/* LICENSE 30/* .ad 31/* .fi 32/* The Secure Mailer license must be distributed with this software. 33/* AUTHOR(S) 34/* Wietse Venema 35/* IBM T.J. Watson Research 36/* P.O. Box 704 37/* Yorktown Heights, NY 10598, USA 38/* 39/* Dean C. Strik 40/* Department ICT 41/* Eindhoven University of Technology 42/* P.O. Box 513 43/* 5600 MB Eindhoven, Netherlands 44/* E-mail: <dean@ipnet6.org> 45/*--*/ 46 47/* System library. */ 48 49#include <sys_defs.h> 50#include <sys/socket.h> 51#include <sys/time.h> 52#include <netinet/in.h> 53#include <net/if.h> 54#include <sys/ioctl.h> 55#include <arpa/inet.h> 56#include <unistd.h> 57#ifdef USE_SYS_SOCKIO_H 58#include <sys/sockio.h> 59#endif 60#include <errno.h> 61#include <string.h> 62#ifdef HAS_IPV6 /* Linux only? */ 63#include <netdb.h> 64#include <stdio.h> 65#endif 66#ifdef HAVE_GETIFADDRS 67#include <ifaddrs.h> 68#endif 69 70/* Utility library. */ 71 72#include <msg.h> 73#include <mymalloc.h> 74#include <vstring.h> 75#include <inet_addr_list.h> 76#include <inet_addr_local.h> 77#include <myaddrinfo.h> 78#include <sock_addr.h> 79#include <mask_addr.h> 80#include <hex_code.h> 81 82 /* 83 * Postfix needs its own interface address information to determine whether 84 * or not it is an MX host for some destination; without this information, 85 * mail would loop between MX hosts. Postfix also needs its interface 86 * addresses to figure out whether or not it is final destination for 87 * addresses of the form username@[ipaddress]. 88 * 89 * Postfix needs its own interface netmask information when no explicit 90 * mynetworks setting is given in main.cf, and "mynetworks_style = subnet". 91 * The mynetworks parameter controls, among others, what mail clients are 92 * allowed to relay mail through Postfix. 93 * 94 * Different systems have different ways to find out this information. We will 95 * therefore use OS dependent methods. An overview: 96 * 97 * - Use getifaddrs() when available. This supports both IPv4/IPv6 addresses. 98 * The implementation however is not present in all major operating systems. 99 * 100 * - Use SIOCGLIFCONF when available. This supports both IPv4/IPv6 addresses. 101 * With SIOCGLIFNETMASK we can obtain the netmask for either address family. 102 * Again, this is not present in all major operating systems. 103 * 104 * - On Linux, glibc's getifaddrs(3) has returned IPv4 information for some 105 * time, but IPv6 information was not returned until 2.3.3. With older Linux 106 * versions we get IPv4 interface information with SIOCGIFCONF, and read 107 * IPv6 address/prefix information from a file in the /proc filesystem. 108 * 109 * - On other systems we expect SIOCGIFCONF to return IPv6 addresses. Since 110 * SIOCGIFNETMASK does not work reliably for IPv6 addresses, we always set 111 * the prefix length to /128 (host), and expect the user to configure a more 112 * appropriate mynetworks setting if needed. 113 * 114 * XXX: Each lookup method is implemented by its own function, so we duplicate 115 * some code. In this case, I think this is better than really drowning in 116 * the #ifdefs... 117 * 118 * -- Dean Strik (dcs) 119 */ 120 121#ifndef HAVE_GETIFADDRS 122 123/* ial_socket - make socket for ioctl() operations */ 124 125static int ial_socket(int af) 126{ 127 const char *myname = "inet_addr_local[socket]"; 128 int sock; 129 130 /* 131 * The host may not be actually configured with IPv6. When IPv6 support 132 * is not actually in the kernel, don't consider failure to create an 133 * IPv6 socket as fatal. This could be tuned better though. For other 134 * families, the error is fatal. 135 * 136 * XXX Now that Postfix controls protocol support centrally with the 137 * inet_proto(3) module, this workaround should no longer be needed. 138 */ 139 if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) { 140#ifdef HAS_IPV6 141 if (af == AF_INET6) { 142 if (msg_verbose) 143 msg_warn("%s: socket: %m", myname); 144 return (-1); 145 } 146#endif 147 msg_fatal("%s: socket: %m", myname); 148 } 149 return (sock); 150} 151 152#endif 153 154#ifdef HAVE_GETIFADDRS 155 156/* 157 * The getifaddrs(3) function, introduced by BSD/OS, provides a 158 * platform-independent way of requesting interface addresses, 159 * including IPv6 addresses. The implementation however is not 160 * present in all major operating systems. 161 */ 162 163/* ial_getifaddrs - determine IP addresses using getifaddrs(3) */ 164 165static int ial_getifaddrs(INET_ADDR_LIST *addr_list, 166 INET_ADDR_LIST *mask_list, 167 int af) 168{ 169 const char *myname = "inet_addr_local[getifaddrs]"; 170 struct ifaddrs *ifap, *ifa; 171 struct sockaddr *sa, *sam; 172 173 if (getifaddrs(&ifap) < 0) 174 msg_fatal("%s: getifaddrs: %m", myname); 175 176 /* 177 * Get the address of each IP network interface. According to BIND we 178 * must include interfaces that are down because the machine may still 179 * receive packets for that address (yes, via some other interface). 180 * Having no way to verify this claim on every machine, I will give them 181 * the benefit of the doubt. 182 * 183 * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces; 184 * fixed by replacing IFF_RUNNING by IFF_UP. 185 * 186 * FIX 200501: The IPV6 patch did not skip wild-card interface addresses 187 * (tested on FreeBSD). 188 */ 189 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 190 if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0) 191 continue; 192 sa = ifa->ifa_addr; 193 if (af != AF_UNSPEC && sa->sa_family != af) 194 continue; 195 sam = ifa->ifa_netmask; 196 if (sam == 0) { 197 /* XXX In mynetworks, a null netmask would match everyone. */ 198 msg_warn("ignoring interface with null netmask, address family %d", 199 sa->sa_family); 200 continue; 201 } 202 switch (sa->sa_family) { 203 case AF_INET: 204 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) 205 continue; 206 break; 207#ifdef HAS_IPV6 208 case AF_INET6: 209 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) 210 continue; 211 break; 212#endif 213 default: 214 continue; 215 } 216 217 inet_addr_list_append(addr_list, sa); 218 if (mask_list != 0) { 219 220 /* 221 * Unfortunately, sa_len/sa_family may be broken in the netmask 222 * sockaddr structure. We must fix this manually to have correct 223 * addresses. --dcs 224 */ 225#ifdef HAS_SA_LEN 226 sam->sa_len = sa->sa_family == AF_INET6 ? 227 sizeof(struct sockaddr_in6) : 228 sizeof(struct sockaddr_in); 229#endif 230 sam->sa_family = sa->sa_family; 231 inet_addr_list_append(mask_list, sam); 232 } 233 } 234 freeifaddrs(ifap); 235 return (0); 236} 237 238#elif defined(HAS_SIOCGLIF) /* HAVE_GETIFADDRS */ 239 240/* 241 * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris 242 * and HP/UX operating systems. The data is stored in sockaddr_storage 243 * structure. Both IPv4 and IPv6 addresses are returned though these 244 * calls. 245 */ 246#define NEXT_INTERFACE(lifr) (lifr + 1) 247#define LIFREQ_SIZE(lifr) sizeof(lifr[0]) 248 249/* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */ 250 251static int ial_siocglif(INET_ADDR_LIST *addr_list, 252 INET_ADDR_LIST *mask_list, 253 int af) 254{ 255 const char *myname = "inet_addr_local[siocglif]"; 256 struct lifconf lifc; 257 struct lifreq *lifr; 258 struct lifreq *lifr_mask; 259 struct lifreq *the_end; 260 struct sockaddr *sa; 261 int sock; 262 VSTRING *buf; 263 264 /* 265 * See also comments in ial_siocgif() 266 */ 267 if (af != AF_INET && af != AF_INET6) 268 msg_fatal("%s: address family was %d, must be AF_INET (%d) or " 269 "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6); 270 sock = ial_socket(af); 271 if (sock < 0) 272 return (0); 273 buf = vstring_alloc(1024); 274 for (;;) { 275 memset(&lifc, 0, sizeof(lifc)); 276 lifc.lifc_family = AF_UNSPEC; /* XXX Why??? */ 277 lifc.lifc_len = vstring_avail(buf); 278 lifc.lifc_buf = vstring_str(buf); 279 if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) { 280 if (errno != EINVAL) 281 msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname); 282 } else if (lifc.lifc_len < vstring_avail(buf) / 2) 283 break; 284 VSTRING_SPACE(buf, vstring_avail(buf) * 2); 285 } 286 287 the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len); 288 for (lifr = lifc.lifc_req; lifr < the_end;) { 289 sa = (struct sockaddr *) &lifr->lifr_addr; 290 if (sa->sa_family != af) { 291 lifr = NEXT_INTERFACE(lifr); 292 continue; 293 } 294 if (af == AF_INET) { 295 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) { 296 lifr = NEXT_INTERFACE(lifr); 297 continue; 298 } 299 } 300#ifdef HAS_IPV6 301 else if (af == AF_INET6) { 302 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) { 303 lifr = NEXT_INTERFACE(lifr); 304 continue; 305 } 306 } 307#endif 308 inet_addr_list_append(addr_list, sa); 309 if (mask_list) { 310 lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq)); 311 memcpy((void *) lifr_mask, (void *) lifr, sizeof(struct lifreq)); 312 if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0) 313 msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname); 314 /* XXX: Check whether sa_len/family are honoured --dcs */ 315 inet_addr_list_append(mask_list, 316 (struct sockaddr *) &lifr_mask->lifr_addr); 317 myfree((void *) lifr_mask); 318 } 319 lifr = NEXT_INTERFACE(lifr); 320 } 321 vstring_free(buf); 322 (void) close(sock); 323 return (0); 324} 325 326#else /* HAVE_SIOCGLIF */ 327 328/* 329 * The classic SIOCGIF* ioctls. Modern BSD operating systems will 330 * also return IPv6 addresses through these structure. Note however 331 * that recent versions of these operating systems have getifaddrs. 332 */ 333#if defined(_SIZEOF_ADDR_IFREQ) 334#define NEXT_INTERFACE(ifr) ((struct ifreq *) \ 335 ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr))) 336#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr) 337#elif defined(HAS_SA_LEN) 338#define NEXT_INTERFACE(ifr) ((struct ifreq *) \ 339 ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)) 340#define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len) 341#else 342#define NEXT_INTERFACE(ifr) (ifr + 1) 343#define IFREQ_SIZE(ifr) sizeof(ifr[0]) 344#endif 345 346/* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */ 347 348static int ial_siocgif(INET_ADDR_LIST *addr_list, 349 INET_ADDR_LIST *mask_list, 350 int af) 351{ 352 const char *myname = "inet_addr_local[siocgif]"; 353 struct in_addr addr; 354 struct ifconf ifc; 355 struct ifreq *ifr; 356 struct ifreq *ifr_mask; 357 struct ifreq *the_end; 358 int sock; 359 VSTRING *buf; 360 361 /* 362 * Get the network interface list. XXX The socket API appears to have no 363 * function that returns the number of network interfaces, so we have to 364 * guess how much space is needed to store the result. 365 * 366 * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as 367 * possible, leaving it up to the application to repeat the request with 368 * a larger buffer if the result caused a tight fit. 369 * 370 * Other systems, such as Solaris 2.5, generate an EINVAL error when the 371 * buffer is too small for the entire result. Workaround: ignore EINVAL 372 * errors and repeat the request with a larger buffer. The downside is 373 * that the program can run out of memory due to a non-memory problem, 374 * making it more difficult than necessary to diagnose the real problem. 375 */ 376 sock = ial_socket(af); 377 if (sock < 0) 378 return (0); 379 buf = vstring_alloc(1024); 380 for (;;) { 381 ifc.ifc_len = vstring_avail(buf); 382 ifc.ifc_buf = vstring_str(buf); 383 if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) { 384 if (errno != EINVAL) 385 msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname); 386 } else if (ifc.ifc_len < vstring_avail(buf) / 2) 387 break; 388 VSTRING_SPACE(buf, vstring_avail(buf) * 2); 389 } 390 391 the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 392 for (ifr = ifc.ifc_req; ifr < the_end;) { 393 if (ifr->ifr_addr.sa_family != af) { 394 ifr = NEXT_INTERFACE(ifr); 395 continue; 396 } 397 if (af == AF_INET) { 398 addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr; 399 if (addr.s_addr != INADDR_ANY) { 400 inet_addr_list_append(addr_list, &ifr->ifr_addr); 401 if (mask_list) { 402 ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr)); 403 memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr)); 404 if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0) 405 msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname); 406 407 /* 408 * Note that this SIOCGIFNETMASK has truly screwed up the 409 * contents of sa_len/sa_family. We must fix this 410 * manually to have correct addresses. --dcs 411 */ 412 ifr_mask->ifr_addr.sa_family = af; 413#ifdef HAS_SA_LEN 414 ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in); 415#endif 416 inet_addr_list_append(mask_list, &ifr_mask->ifr_addr); 417 myfree((void *) ifr_mask); 418 } 419 } 420 } 421#ifdef HAS_IPV6 422 else if (af == AF_INET6) { 423 struct sockaddr *sa; 424 425 sa = SOCK_ADDR_PTR(&ifr->ifr_addr); 426 if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) { 427 inet_addr_list_append(addr_list, sa); 428 if (mask_list) { 429 /* XXX Assume /128 for everything */ 430 struct sockaddr_in6 mask6; 431 432 mask6 = *SOCK_ADDR_IN6_PTR(sa); 433 memset((void *) &mask6.sin6_addr, ~0, 434 sizeof(mask6.sin6_addr)); 435 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6)); 436 } 437 } 438 } 439#endif 440 ifr = NEXT_INTERFACE(ifr); 441 } 442 vstring_free(buf); 443 (void) close(sock); 444 return (0); 445} 446 447#endif /* HAVE_SIOCGLIF */ 448 449#ifdef HAS_PROCNET_IFINET6 450 451/* 452 * Older Linux versions lack proper calls to retrieve IPv6 interface 453 * addresses. Instead, the addresses can be read from a file in the 454 * /proc tree. The most important issue with this approach however 455 * is that the /proc tree may not always be available, for example 456 * in a chrooted environment or in "hardened" (sic) installations. 457 */ 458 459/* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */ 460 461static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list, 462 INET_ADDR_LIST *mask_list) 463{ 464 const char *myname = "inet_addr_local[procnet_ifinet6]"; 465 FILE *fp; 466 char buf[BUFSIZ]; 467 unsigned plen; 468 VSTRING *addrbuf; 469 struct sockaddr_in6 addr; 470 struct sockaddr_in6 mask; 471 472 /* 473 * Example: 00000000000000000000000000000001 01 80 10 80 lo 474 * 475 * Fields: address, interface index, prefix length, scope value 476 * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name. 477 * 478 * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected 479 * input. Use fgets() + sscanf() instead. 480 */ 481 if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) { 482 addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1); 483 memset((void *) &addr, 0, sizeof(addr)); 484 addr.sin6_family = AF_INET6; 485#ifdef HAS_SA_LEN 486 addr.sin6_len = sizeof(addr); 487#endif 488 mask = addr; 489 while (fgets(buf, sizeof(buf), fp) != 0) { 490 /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */ 491 if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0 492 || sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1 493 || plen > MAI_V6ADDR_BITS) { 494 msg_warn("unexpected data in %s - skipping IPv6 configuration", 495 _PATH_PROCNET_IFINET6); 496 break; 497 } 498 /* vstring_str(addrbuf) has worst-case alignment. */ 499 addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf); 500 inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr)); 501 502 memset((void *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr)); 503 mask_addr((unsigned char *) &mask.sin6_addr, 504 sizeof(mask.sin6_addr), plen); 505 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask)); 506 } 507 vstring_free(addrbuf); 508 fclose(fp); /* FIX 200501 */ 509 } else { 510 msg_warn("can't open %s (%m) - skipping IPv6 configuration", 511 _PATH_PROCNET_IFINET6); 512 } 513 return (0); 514} 515 516#endif /* HAS_PROCNET_IFINET6 */ 517 518/* inet_addr_local - find all IP addresses for this host */ 519 520int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list, 521 unsigned *addr_family_list) 522{ 523 const char *myname = "inet_addr_local"; 524 int initial_count = addr_list->used; 525 unsigned family; 526 int count; 527 528 while ((family = *addr_family_list++) != 0) { 529 530 /* 531 * IP Version 4 532 */ 533 if (family == AF_INET) { 534 count = addr_list->used; 535#if defined(HAVE_GETIFADDRS) 536 ial_getifaddrs(addr_list, mask_list, AF_INET); 537#elif defined (HAS_SIOCGLIF) 538 ial_siocglif(addr_list, mask_list, AF_INET); 539#else 540 ial_siocgif(addr_list, mask_list, AF_INET); 541#endif 542 if (msg_verbose) 543 msg_info("%s: configured %d IPv4 addresses", 544 myname, addr_list->used - count); 545 } 546 547 /* 548 * IP Version 6 549 */ 550#ifdef HAS_IPV6 551 else if (family == AF_INET6) { 552 count = addr_list->used; 553#if defined(HAVE_GETIFADDRS) 554 ial_getifaddrs(addr_list, mask_list, AF_INET6); 555#elif defined(HAS_PROCNET_IFINET6) 556 ial_procnet_ifinet6(addr_list, mask_list); 557#elif defined(HAS_SIOCGLIF) 558 ial_siocglif(addr_list, mask_list, AF_INET6); 559#else 560 ial_siocgif(addr_list, mask_list, AF_INET6); 561#endif 562 if (msg_verbose) 563 msg_info("%s: configured %d IPv6 addresses", myname, 564 addr_list->used - count); 565 } 566#endif 567 568 /* 569 * Something's not right. 570 */ 571 else 572 msg_panic("%s: unknown address family %d", myname, family); 573 } 574 return (addr_list->used - initial_count); 575} 576 577#ifdef TEST 578 579#include <string.h> 580#include <vstream.h> 581#include <msg_vstream.h> 582#include <inet_proto.h> 583 584int main(int unused_argc, char **argv) 585{ 586 INET_ADDR_LIST addr_list; 587 INET_ADDR_LIST mask_list; 588 MAI_HOSTADDR_STR hostaddr; 589 MAI_HOSTADDR_STR hostmask; 590 struct sockaddr *sa; 591 int i; 592 INET_PROTO_INFO *proto_info; 593 594 msg_vstream_init(argv[0], VSTREAM_ERR); 595 msg_verbose = 1; 596 597 proto_info = inet_proto_init(argv[0], 598 argv[1] ? argv[1] : INET_PROTO_NAME_ALL); 599 inet_addr_list_init(&addr_list); 600 inet_addr_list_init(&mask_list); 601 inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list); 602 603 if (addr_list.used == 0) 604 msg_fatal("cannot find any active network interfaces"); 605 606 if (addr_list.used == 1) 607 msg_warn("found only one active network interface"); 608 609 for (i = 0; i < addr_list.used; i++) { 610 sa = SOCK_ADDR_PTR(addr_list.addrs + i); 611 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), 612 &hostaddr, (MAI_SERVPORT_STR *) 0, 0); 613 sa = SOCK_ADDR_PTR(mask_list.addrs + i); 614 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), 615 &hostmask, (MAI_SERVPORT_STR *) 0, 0); 616 vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf); 617 vstream_fflush(VSTREAM_OUT); 618 } 619 inet_addr_list_free(&addr_list); 620 inet_addr_list_free(&mask_list); 621 return (0); 622} 623 624#endif 625