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