inet_addr_local.c revision 1.3
1/* $NetBSD: inet_addr_local.c,v 1.3 2010/06/17 18:18:16 tron 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/* ial_socket - make socket for ioctl() operations */ 122 123static int ial_socket(int af) 124{ 125 const char *myname = "inet_addr_local[socket]"; 126 int sock; 127 128 /* 129 * The host may not be actually configured with IPv6. When IPv6 support 130 * is not actually in the kernel, don't consider failure to create an 131 * IPv6 socket as fatal. This could be tuned better though. For other 132 * families, the error is fatal. 133 * 134 * XXX Now that Postfix controls protocol support centrally with the 135 * inet_proto(3) module, this workaround should no longer be needed. 136 */ 137 if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) { 138#ifdef HAS_IPV6 139 if (af == AF_INET6) { 140 if (msg_verbose) 141 msg_warn("%s: socket: %m", myname); 142 return (-1); 143 } 144#endif 145 msg_fatal("%s: socket: %m", myname); 146 } 147 return (sock); 148} 149 150#ifdef HAVE_GETIFADDRS 151 152/* 153 * The getifaddrs(3) function, introduced by BSD/OS, provides a 154 * platform-independent way of requesting interface addresses, 155 * including IPv6 addresses. The implementation however is not 156 * present in all major operating systems. 157 */ 158 159/* ial_getifaddrs - determine IP addresses using getifaddrs(3) */ 160 161static int ial_getifaddrs(INET_ADDR_LIST *addr_list, 162 INET_ADDR_LIST *mask_list, 163 int af) 164{ 165 const char *myname = "inet_addr_local[getifaddrs]"; 166 struct ifaddrs *ifap, *ifa; 167 struct sockaddr *sa, *sam; 168 169 if (getifaddrs(&ifap) < 0) 170 msg_fatal("%s: getifaddrs: %m", myname); 171 172 /* 173 * Get the address of each IP network interface. According to BIND we 174 * must include interfaces that are down because the machine may still 175 * receive packets for that address (yes, via some other interface). 176 * Having no way to verify this claim on every machine, I will give them 177 * the benefit of the doubt. 178 * 179 * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces; 180 * fixed by replacing IFF_RUNNING by IFF_UP. 181 * 182 * FIX 200501: The IPV6 patch did not skip wild-card interface addresses 183 * (tested on FreeBSD). 184 */ 185 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 186 if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0) 187 continue; 188 sa = ifa->ifa_addr; 189 if (af != AF_UNSPEC && sa->sa_family != af) 190 continue; 191 sam = ifa->ifa_netmask; 192 if (sam == 0) { 193 /* XXX In mynetworks, a null netmask would match everyone. */ 194 msg_warn("ignoring interface with null netmask, address family %d", 195 sa->sa_family); 196 continue; 197 } 198 switch (sa->sa_family) { 199 case AF_INET: 200 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) 201 continue; 202 break; 203#ifdef HAS_IPV6 204 case AF_INET6: 205 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) 206 continue; 207 break; 208#endif 209 default: 210 continue; 211 } 212 213 inet_addr_list_append(addr_list, sa); 214 if (mask_list != 0) { 215 216 /* 217 * Unfortunately, sa_len/sa_family may be broken in the netmask 218 * sockaddr structure. We must fix this manually to have correct 219 * addresses. --dcs 220 */ 221#ifdef HAS_SA_LEN 222 sam->sa_len = sa->sa_family == AF_INET6 ? 223 sizeof(struct sockaddr_in6) : 224 sizeof(struct sockaddr_in); 225#endif 226 sam->sa_family = sa->sa_family; 227 inet_addr_list_append(mask_list, sam); 228 } 229 } 230 freeifaddrs(ifap); 231 return (0); 232} 233 234#endif /* HAVE_GETIFADDRS */ 235 236#ifdef HAS_SIOCGLIF 237 238/* 239 * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris 240 * and HP/UX operating systems. The data is stored in sockaddr_storage 241 * structure. Both IPv4 and IPv6 addresses are returned though these 242 * calls. 243 */ 244#define NEXT_INTERFACE(lifr) (lifr + 1) 245#define LIFREQ_SIZE(lifr) sizeof(lifr[0]) 246 247/* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */ 248 249static int ial_siocglif(INET_ADDR_LIST *addr_list, 250 INET_ADDR_LIST *mask_list, 251 int af) 252{ 253 const char *myname = "inet_addr_local[siocglif]"; 254 struct lifconf lifc; 255 struct lifreq *lifr; 256 struct lifreq *lifr_mask; 257 struct lifreq *the_end; 258 struct sockaddr *sa; 259 int sock; 260 VSTRING *buf; 261 262 /* 263 * See also comments in ial_siocgif() 264 */ 265 if (af != AF_INET && af != AF_INET6) 266 msg_fatal("%s: address family was %d, must be AF_INET (%d) or " 267 "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6); 268 sock = ial_socket(af); 269 if (sock < 0) 270 return (0); 271 buf = vstring_alloc(1024); 272 for (;;) { 273 memset(&lifc, 0, sizeof(lifc)); 274 lifc.lifc_family = AF_UNSPEC; /* XXX Why??? */ 275 lifc.lifc_len = vstring_avail(buf); 276 lifc.lifc_buf = vstring_str(buf); 277 if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) { 278 if (errno != EINVAL) 279 msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname); 280 } else if (lifc.lifc_len < vstring_avail(buf) / 2) 281 break; 282 VSTRING_SPACE(buf, vstring_avail(buf) * 2); 283 } 284 285 the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len); 286 for (lifr = lifc.lifc_req; lifr < the_end;) { 287 sa = (struct sockaddr *) & lifr->lifr_addr; 288 if (sa->sa_family != af) { 289 lifr = NEXT_INTERFACE(lifr); 290 continue; 291 } 292 if (af == AF_INET) { 293 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) { 294 lifr = NEXT_INTERFACE(lifr); 295 continue; 296 } 297 } 298#ifdef HAS_IPV6 299 else if (af == AF_INET6) { 300 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) { 301 lifr = NEXT_INTERFACE(lifr); 302 continue; 303 } 304 } 305#endif 306 inet_addr_list_append(addr_list, sa); 307 if (mask_list) { 308 lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq)); 309 memcpy((char *) lifr_mask, (char *) lifr, sizeof(struct lifreq)); 310 if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0) 311 msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname); 312 /* XXX: Check whether sa_len/family are honoured --dcs */ 313 inet_addr_list_append(mask_list, 314 (struct sockaddr *) & lifr_mask->lifr_addr); 315 myfree((char *) lifr_mask); 316 } 317 lifr = NEXT_INTERFACE(lifr); 318 } 319 vstring_free(buf); 320 (void) close(sock); 321 return (0); 322} 323 324#else /* HAVE_SIOCGLIF */ 325 326/* 327 * The classic SIOCGIF* ioctls. Modern BSD operating systems will 328 * also return IPv6 addresses through these structure. Note however 329 * that recent versions of these operating systems have getifaddrs. 330 */ 331#if defined(_SIZEOF_ADDR_IFREQ) 332#define NEXT_INTERFACE(ifr) ((struct ifreq *) \ 333 ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr))) 334#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr) 335#elif defined(HAS_SA_LEN) 336#define NEXT_INTERFACE(ifr) ((struct ifreq *) \ 337 ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)) 338#define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len) 339#else 340#define NEXT_INTERFACE(ifr) (ifr + 1) 341#define IFREQ_SIZE(ifr) sizeof(ifr[0]) 342#endif 343 344/* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */ 345 346static int ial_siocgif(INET_ADDR_LIST *addr_list, 347 INET_ADDR_LIST *mask_list, 348 int af) 349{ 350 const char *myname = "inet_addr_local[siocgif]"; 351 struct in_addr addr; 352 struct ifconf ifc; 353 struct ifreq *ifr; 354 struct ifreq *ifr_mask; 355 struct ifreq *the_end; 356 int sock; 357 VSTRING *buf; 358 359 /* 360 * Get the network interface list. XXX The socket API appears to have no 361 * function that returns the number of network interfaces, so we have to 362 * guess how much space is needed to store the result. 363 * 364 * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as 365 * possible, leaving it up to the application to repeat the request with 366 * a larger buffer if the result caused a tight fit. 367 * 368 * Other systems, such as Solaris 2.5, generate an EINVAL error when the 369 * buffer is too small for the entire result. Workaround: ignore EINVAL 370 * errors and repeat the request with a larger buffer. The downside is 371 * that the program can run out of memory due to a non-memory problem, 372 * making it more difficult than necessary to diagnose the real problem. 373 */ 374 sock = ial_socket(af); 375 if (sock < 0) 376 return (0); 377 buf = vstring_alloc(1024); 378 for (;;) { 379 ifc.ifc_len = vstring_avail(buf); 380 ifc.ifc_buf = vstring_str(buf); 381 if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) { 382 if (errno != EINVAL) 383 msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname); 384 } else if (ifc.ifc_len < vstring_avail(buf) / 2) 385 break; 386 VSTRING_SPACE(buf, vstring_avail(buf) * 2); 387 } 388 389 the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 390 for (ifr = ifc.ifc_req; ifr < the_end;) { 391 if (ifr->ifr_addr.sa_family != af) { 392 ifr = NEXT_INTERFACE(ifr); 393 continue; 394 } 395 if (af == AF_INET) { 396 addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr; 397 if (addr.s_addr != INADDR_ANY) { 398 inet_addr_list_append(addr_list, &ifr->ifr_addr); 399 if (mask_list) { 400 ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr)); 401 memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr)); 402 if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0) 403 msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname); 404 405 /* 406 * Note that this SIOCGIFNETMASK has truly screwed up the 407 * contents of sa_len/sa_family. We must fix this 408 * manually to have correct addresses. --dcs 409 */ 410 ifr_mask->ifr_addr.sa_family = af; 411#ifdef HAS_SA_LEN 412 ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in); 413#endif 414 inet_addr_list_append(mask_list, &ifr_mask->ifr_addr); 415 myfree((char *) ifr_mask); 416 } 417 } 418 } 419#ifdef HAS_IPV6 420 else if (af == AF_INET6) { 421 struct sockaddr *sa; 422 423 sa = SOCK_ADDR_PTR(&ifr->ifr_addr); 424 if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) { 425 inet_addr_list_append(addr_list, sa); 426 if (mask_list) { 427 /* XXX Assume /128 for everything */ 428 struct sockaddr_in6 mask6; 429 430 mask6 = *SOCK_ADDR_IN6_PTR(sa); 431 memset((char *) &mask6.sin6_addr, ~0, 432 sizeof(mask6.sin6_addr)); 433 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6)); 434 } 435 } 436 } 437#endif 438 ifr = NEXT_INTERFACE(ifr); 439 } 440 vstring_free(buf); 441 (void) close(sock); 442 return (0); 443} 444 445#endif /* HAVE_SIOCGLIF */ 446 447#ifdef HAS_PROCNET_IFINET6 448 449/* 450 * Older Linux versions lack proper calls to retrieve IPv6 interface 451 * addresses. Instead, the addresses can be read from a file in the 452 * /proc tree. The most important issue with this approach however 453 * is that the /proc tree may not always be available, for example 454 * in a chrooted environment or in "hardened" (sic) installations. 455 */ 456 457/* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */ 458 459static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list, 460 INET_ADDR_LIST *mask_list) 461{ 462 const char *myname = "inet_addr_local[procnet_ifinet6]"; 463 FILE *fp; 464 char buf[BUFSIZ]; 465 unsigned plen; 466 VSTRING *addrbuf; 467 struct sockaddr_in6 addr; 468 struct sockaddr_in6 mask; 469 470 /* 471 * Example: 00000000000000000000000000000001 01 80 10 80 lo 472 * 473 * Fields: address, interface index, prefix length, scope value 474 * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name. 475 * 476 * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected 477 * input. Use fgets() + sscanf() instead. 478 */ 479 if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) { 480 addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1); 481 memset((char *) &addr, 0, sizeof(addr)); 482 addr.sin6_family = AF_INET6; 483#ifdef HAS_SA_LEN 484 addr.sin6_len = sizeof(addr); 485#endif 486 mask = addr; 487 while (fgets(buf, sizeof(buf), fp) != 0) { 488 /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */ 489 if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0 490 || sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1 491 || plen > MAI_V6ADDR_BITS) { 492 msg_warn("unexpected data in %s - skipping IPv6 configuration", 493 _PATH_PROCNET_IFINET6); 494 break; 495 } 496 /* vstring_str(addrbuf) has worst-case alignment. */ 497 addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf); 498 inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr)); 499 500 memset((char *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr)); 501 mask_addr((unsigned char *) &mask.sin6_addr, 502 sizeof(mask.sin6_addr), plen); 503 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask)); 504 } 505 vstring_free(addrbuf); 506 fclose(fp); /* FIX 200501 */ 507 } else { 508 msg_warn("can't open %s (%m) - skipping IPv6 configuration", 509 _PATH_PROCNET_IFINET6); 510 } 511 return (0); 512} 513 514#endif /* HAS_PROCNET_IFINET6 */ 515 516/* inet_addr_local - find all IP addresses for this host */ 517 518int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list, 519 unsigned *addr_family_list) 520{ 521 const char *myname = "inet_addr_local"; 522 int initial_count = addr_list->used; 523 unsigned family; 524 int count; 525 526 while ((family = *addr_family_list++) != 0) { 527 528 /* 529 * IP Version 4 530 */ 531 if (family == AF_INET) { 532 count = addr_list->used; 533#if defined(HAVE_GETIFADDRS) 534 ial_getifaddrs(addr_list, mask_list, AF_INET); 535#elif defined (HAS_SIOCGLIF) 536 ial_siocglif(addr_list, mask_list, AF_INET); 537#else 538 ial_siocgif(addr_list, mask_list, AF_INET); 539#endif 540 if (msg_verbose) 541 msg_info("%s: configured %d IPv4 addresses", 542 myname, addr_list->used - count); 543 } 544 545 /* 546 * IP Version 6 547 */ 548#ifdef HAS_IPV6 549 else if (family == AF_INET6) { 550 count = addr_list->used; 551#if defined(HAVE_GETIFADDRS) 552 ial_getifaddrs(addr_list, mask_list, AF_INET6); 553#elif defined(HAS_PROCNET_IFINET6) 554 ial_procnet_ifinet6(addr_list, mask_list); 555#elif defined(HAS_SIOCGLIF) 556 ial_siocglif(addr_list, mask_list, AF_INET6); 557#else 558 ial_siocgif(addr_list, mask_list, AF_INET6); 559#endif 560 if (msg_verbose) 561 msg_info("%s: configured %d IPv6 addresses", myname, 562 addr_list->used - count); 563 } 564#endif 565 566 /* 567 * Something's not right. 568 */ 569 else 570 msg_panic("%s: unknown address family %d", myname, family); 571 } 572 return (addr_list->used - initial_count); 573} 574 575#ifdef TEST 576 577#include <string.h> 578#include <vstream.h> 579#include <msg_vstream.h> 580#include <inet_proto.h> 581 582int main(int unused_argc, char **argv) 583{ 584 INET_ADDR_LIST addr_list; 585 INET_ADDR_LIST mask_list; 586 MAI_HOSTADDR_STR hostaddr; 587 MAI_HOSTADDR_STR hostmask; 588 struct sockaddr *sa; 589 int i; 590 INET_PROTO_INFO *proto_info; 591 592 msg_vstream_init(argv[0], VSTREAM_ERR); 593 msg_verbose = 1; 594 595 proto_info = inet_proto_init(argv[0], 596 argv[1] ? argv[1] : INET_PROTO_NAME_ALL); 597 inet_addr_list_init(&addr_list); 598 inet_addr_list_init(&mask_list); 599 inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list); 600 601 if (addr_list.used == 0) 602 msg_fatal("cannot find any active network interfaces"); 603 604 if (addr_list.used == 1) 605 msg_warn("found only one active network interface"); 606 607 for (i = 0; i < addr_list.used; i++) { 608 sa = SOCK_ADDR_PTR(addr_list.addrs + i); 609 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), 610 &hostaddr, (MAI_SERVPORT_STR *) 0, 0); 611 sa = SOCK_ADDR_PTR(mask_list.addrs + i); 612 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), 613 &hostmask, (MAI_SERVPORT_STR *) 0, 0); 614 vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf); 615 vstream_fflush(VSTREAM_OUT); 616 } 617 inet_addr_list_free(&addr_list); 618 inet_addr_list_free(&mask_list); 619 return (0); 620} 621 622#endif 623