7 /* determine if IP address is local
9 /* #include <inet_addr_local.h>
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;
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.
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.
24 /* The addr_family_list specifies one or more of AF_INET or AF_INET6.
26 /* Fatal errors: out of memory.
28 /* inet_addr_list(3) address list management
32 /* The Secure Mailer license must be distributed with this software.
35 /* IBM T.J. Watson Research
37 /* Yorktown Heights, NY 10598, USA
41 /* Eindhoven University of Technology
43 /* 5600 MB Eindhoven, Netherlands
44 /* E-mail: <dean@ipnet6.org>
50 #include <sys/socket.h>
52 #include <netinet/in.h>
54 #include <sys/ioctl.h>
55 #include <arpa/inet.h>
57 #ifdef USE_SYS_SOCKIO_H
58 #include <sys/sockio.h>
62 #ifdef HAS_IPV6 /* Linux only? */
66 #ifdef HAVE_GETIFADDRS
70 /* Utility library. */
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>
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].
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.
94 * Different systems have different ways to find out this information. We will
95 * therefore use OS dependent methods. An overview:
97 * - Use getifaddrs() when available. This supports both IPv4/IPv6 addresses.
98 * The implementation however is not present in all major operating systems.
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.
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.
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.
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
118 * -- Dean Strik (dcs)
121 /* ial_socket - make socket for ioctl() operations */
123 static int ial_socket(int af
)
125 const char *myname
= "inet_addr_local[socket]";
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.
134 * XXX Now that Postfix controls protocol support centrally with the
135 * inet_proto(3) module, this workaround should no longer be needed.
137 if ((sock
= socket(af
, SOCK_DGRAM
, 0)) < 0) {
139 if (af
== AF_INET6
) {
141 msg_warn("%s: socket: %m", myname
);
145 msg_fatal("%s: socket: %m", myname
);
150 #ifdef HAVE_GETIFADDRS
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.
159 /* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
161 static int ial_getifaddrs(INET_ADDR_LIST
*addr_list
,
162 INET_ADDR_LIST
*mask_list
,
165 const char *myname
= "inet_addr_local[getifaddrs]";
166 struct ifaddrs
*ifap
, *ifa
;
167 struct sockaddr
*sa
, *sam
;
169 if (getifaddrs(&ifap
) < 0)
170 msg_fatal("%s: getifaddrs: %m", myname
);
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.
179 * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
180 * fixed by replacing IFF_RUNNING by IFF_UP.
182 * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
183 * (tested on FreeBSD).
185 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
186 if (!(ifa
->ifa_flags
& IFF_UP
) || ifa
->ifa_addr
== 0)
189 sam
= ifa
->ifa_netmask
;
190 if (af
!= AF_UNSPEC
&& sa
->sa_family
!= af
)
192 switch (sa
->sa_family
) {
194 if (SOCK_ADDR_IN_ADDR(sa
).s_addr
== INADDR_ANY
)
199 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa
)))
207 inet_addr_list_append(addr_list
, sa
);
208 if (mask_list
!= 0) {
211 * Unfortunately, sa_len/sa_family may be broken in the netmask
212 * sockaddr structure. We must fix this manually to have correct
216 sam
->sa_len
= sa
->sa_family
== AF_INET6
?
217 sizeof(struct sockaddr_in6
) :
218 sizeof(struct sockaddr_in
);
220 sam
->sa_family
= sa
->sa_family
;
221 inet_addr_list_append(mask_list
, sam
);
228 #endif /* HAVE_GETIFADDRS */
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
238 #define NEXT_INTERFACE(lifr) (lifr + 1)
239 #define LIFREQ_SIZE(lifr) sizeof(lifr[0])
241 /* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
243 static int ial_siocglif(INET_ADDR_LIST
*addr_list
,
244 INET_ADDR_LIST
*mask_list
,
247 const char *myname
= "inet_addr_local[siocglif]";
250 struct lifreq
*lifr_mask
;
251 struct lifreq
*the_end
;
257 * See also comments in ial_siocgif()
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
);
265 buf
= vstring_alloc(1024);
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) {
273 msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname
);
274 } else if (lifc
.lifc_len
< vstring_avail(buf
) / 2)
276 VSTRING_SPACE(buf
, vstring_avail(buf
) * 2);
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
);
287 if (SOCK_ADDR_IN_ADDR(sa
).s_addr
== INADDR_ANY
) {
288 lifr
= NEXT_INTERFACE(lifr
);
292 } else if (af
== AF_INET6
) {
293 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa
))) {
294 lifr
= NEXT_INTERFACE(lifr
);
299 inet_addr_list_append(addr_list
, sa
);
301 lifr_mask
= (struct lifreq
*) mymalloc(sizeof(struct lifreq
));
302 memcpy((char *) lifr_mask
, (char *) lifr
, sizeof(struct lifreq
));
303 if (ioctl(sock
, SIOCGLIFNETMASK
, lifr_mask
) < 0)
304 msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname
);
305 /* XXX: Check whether sa_len/family are honoured --dcs */
306 inet_addr_list_append(mask_list
,
307 (struct sockaddr
*) & lifr_mask
->lifr_addr
);
308 myfree((char *) lifr_mask
);
310 lifr
= NEXT_INTERFACE(lifr
);
317 #else /* HAVE_SIOCGLIF */
320 * The classic SIOCGIF* ioctls. Modern BSD operating systems will
321 * also return IPv6 addresses through these structure. Note however
322 * that recent versions of these operating systems have getifaddrs.
324 #if defined(_SIZEOF_ADDR_IFREQ)
325 #define NEXT_INTERFACE(ifr) ((struct ifreq *) \
326 ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
327 #define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
328 #elif defined(HAS_SA_LEN)
329 #define NEXT_INTERFACE(ifr) ((struct ifreq *) \
330 ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
331 #define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
333 #define NEXT_INTERFACE(ifr) (ifr + 1)
334 #define IFREQ_SIZE(ifr) sizeof(ifr[0])
337 /* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
339 static int ial_siocgif(INET_ADDR_LIST
*addr_list
,
340 INET_ADDR_LIST
*mask_list
,
343 const char *myname
= "inet_addr_local[siocgif]";
347 struct ifreq
*ifr_mask
;
348 struct ifreq
*the_end
;
353 * Get the network interface list. XXX The socket API appears to have no
354 * function that returns the number of network interfaces, so we have to
355 * guess how much space is needed to store the result.
357 * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
358 * possible, leaving it up to the application to repeat the request with
359 * a larger buffer if the result caused a tight fit.
361 * Other systems, such as Solaris 2.5, generate an EINVAL error when the
362 * buffer is too small for the entire result. Workaround: ignore EINVAL
363 * errors and repeat the request with a larger buffer. The downside is
364 * that the program can run out of memory due to a non-memory problem,
365 * making it more difficult than necessary to diagnose the real problem.
367 sock
= ial_socket(af
);
370 buf
= vstring_alloc(1024);
372 ifc
.ifc_len
= vstring_avail(buf
);
373 ifc
.ifc_buf
= vstring_str(buf
);
374 if (ioctl(sock
, SIOCGIFCONF
, (char *) &ifc
) < 0) {
376 msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname
);
377 } else if (ifc
.ifc_len
< vstring_avail(buf
) / 2)
379 VSTRING_SPACE(buf
, vstring_avail(buf
) * 2);
382 the_end
= (struct ifreq
*) (ifc
.ifc_buf
+ ifc
.ifc_len
);
383 for (ifr
= ifc
.ifc_req
; ifr
< the_end
;) {
384 if (ifr
->ifr_addr
.sa_family
!= af
) {
385 ifr
= NEXT_INTERFACE(ifr
);
389 addr
= ((struct sockaddr_in
*) & ifr
->ifr_addr
)->sin_addr
;
390 if (addr
.s_addr
!= INADDR_ANY
) {
391 inet_addr_list_append(addr_list
, &ifr
->ifr_addr
);
393 ifr_mask
= (struct ifreq
*) mymalloc(IFREQ_SIZE(ifr
));
394 memcpy((char *) ifr_mask
, (char *) ifr
, IFREQ_SIZE(ifr
));
395 if (ioctl(sock
, SIOCGIFNETMASK
, ifr_mask
) < 0)
396 msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname
);
399 * Note that this SIOCGIFNETMASK has truly screwed up the
400 * contents of sa_len/sa_family. We must fix this
401 * manually to have correct addresses. --dcs
403 ifr_mask
->ifr_addr
.sa_family
= af
;
405 ifr_mask
->ifr_addr
.sa_len
= sizeof(struct sockaddr_in
);
407 inet_addr_list_append(mask_list
, &ifr_mask
->ifr_addr
);
408 myfree((char *) ifr_mask
);
413 else if (af
== AF_INET6
) {
416 sa
= SOCK_ADDR_PTR(&ifr
->ifr_addr
);
417 if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa
)))) {
418 inet_addr_list_append(addr_list
, sa
);
420 /* XXX Assume /128 for everything */
421 struct sockaddr_in6 mask6
;
423 mask6
= *SOCK_ADDR_IN6_PTR(sa
);
424 memset((char *) &mask6
.sin6_addr
, ~0,
425 sizeof(mask6
.sin6_addr
));
426 inet_addr_list_append(mask_list
, SOCK_ADDR_PTR(&mask6
));
431 ifr
= NEXT_INTERFACE(ifr
);
438 #endif /* HAVE_SIOCGLIF */
440 #ifdef HAS_PROCNET_IFINET6
443 * Older Linux versions lack proper calls to retrieve IPv6 interface
444 * addresses. Instead, the addresses can be read from a file in the
445 * /proc tree. The most important issue with this approach however
446 * is that the /proc tree may not always be available, for example
447 * in a chrooted environment or in "hardened" (sic) installations.
450 /* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
452 static int ial_procnet_ifinet6(INET_ADDR_LIST
*addr_list
,
453 INET_ADDR_LIST
*mask_list
)
455 const char *myname
= "inet_addr_local[procnet_ifinet6]";
460 struct sockaddr_in6 addr
;
461 struct sockaddr_in6 mask
;
464 * Example: 00000000000000000000000000000001 01 80 10 80 lo
466 * Fields: address, interface index, prefix length, scope value
467 * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
469 * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
470 * input. Use fgets() + sscanf() instead.
472 if ((fp
= fopen(_PATH_PROCNET_IFINET6
, "r")) != 0) {
473 addrbuf
= vstring_alloc(MAI_V6ADDR_BYTES
+ 1);
474 memset((char *) &addr
, 0, sizeof(addr
));
475 addr
.sin6_family
= AF_INET6
;
477 addr
.sin6_len
= sizeof(addr
);
480 while (fgets(buf
, sizeof(buf
), fp
) != 0) {
481 /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
482 if (hex_decode(addrbuf
, buf
, MAI_V6ADDR_BYTES
* 2) == 0
483 || sscanf(buf
+ MAI_V6ADDR_BYTES
* 2, " %*x %x", &plen
) != 1
484 || plen
> MAI_V6ADDR_BITS
) {
485 msg_warn("unexpected data in %s - skipping IPv6 configuration",
486 _PATH_PROCNET_IFINET6
);
489 /* vstring_str(addrbuf) has worst-case alignment. */
490 addr
.sin6_addr
= *(struct in6_addr
*) vstring_str(addrbuf
);
491 inet_addr_list_append(addr_list
, SOCK_ADDR_PTR(&addr
));
493 memset((char *) &mask
.sin6_addr
, ~0, sizeof(mask
.sin6_addr
));
494 mask_addr((unsigned char *) &mask
.sin6_addr
,
495 sizeof(mask
.sin6_addr
), plen
);
496 inet_addr_list_append(mask_list
, SOCK_ADDR_PTR(&mask
));
498 vstring_free(addrbuf
);
499 fclose(fp
); /* FIX 200501 */
501 msg_warn("can't open %s (%m) - skipping IPv6 configuration",
502 _PATH_PROCNET_IFINET6
);
507 #endif /* HAS_PROCNET_IFINET6 */
509 /* inet_addr_local - find all IP addresses for this host */
511 int inet_addr_local(INET_ADDR_LIST
*addr_list
, INET_ADDR_LIST
*mask_list
,
512 unsigned *addr_family_list
)
514 const char *myname
= "inet_addr_local";
515 int initial_count
= addr_list
->used
;
519 while ((family
= *addr_family_list
++) != 0) {
524 if (family
== AF_INET
) {
525 count
= addr_list
->used
;
526 #if defined(HAVE_GETIFADDRS)
527 ial_getifaddrs(addr_list
, mask_list
, AF_INET
);
528 #elif defined (HAS_SIOCGLIF)
529 ial_siocglif(addr_list
, mask_list
, AF_INET
);
531 ial_siocgif(addr_list
, mask_list
, AF_INET
);
534 msg_info("%s: configured %d IPv4 addresses",
535 myname
, addr_list
->used
- count
);
542 else if (family
== AF_INET6
) {
543 count
= addr_list
->used
;
544 #if defined(HAVE_GETIFADDRS)
545 ial_getifaddrs(addr_list
, mask_list
, AF_INET6
);
546 #elif defined(HAS_PROCNET_IFINET6)
547 ial_procnet_ifinet6(addr_list
, mask_list
);
548 #elif defined(HAS_SIOCGLIF)
549 ial_siocglif(addr_list
, mask_list
, AF_INET6
);
551 ial_siocgif(addr_list
, mask_list
, AF_INET6
);
554 msg_info("%s: configured %d IPv6 addresses", myname
,
555 addr_list
->used
- count
);
560 * Something's not right.
563 msg_panic("%s: unknown address family %d", myname
, family
);
565 return (addr_list
->used
- initial_count
);
572 #include <msg_vstream.h>
573 #include <inet_proto.h>
575 int main(int unused_argc
, char **argv
)
577 INET_ADDR_LIST addr_list
;
578 INET_ADDR_LIST mask_list
;
579 MAI_HOSTADDR_STR hostaddr
;
580 MAI_HOSTADDR_STR hostmask
;
583 INET_PROTO_INFO
*proto_info
;
585 msg_vstream_init(argv
[0], VSTREAM_ERR
);
588 proto_info
= inet_proto_init(argv
[0], INET_PROTO_NAME_ALL
);
589 inet_addr_list_init(&addr_list
);
590 inet_addr_list_init(&mask_list
);
591 inet_addr_local(&addr_list
, &mask_list
, proto_info
->ai_family_list
);
593 if (addr_list
.used
== 0)
594 msg_fatal("cannot find any active network interfaces");
596 if (addr_list
.used
== 1)
597 msg_warn("found only one active network interface");
599 for (i
= 0; i
< addr_list
.used
; i
++) {
600 sa
= SOCK_ADDR_PTR(addr_list
.addrs
+ i
);
601 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa
), SOCK_ADDR_LEN(sa
),
602 &hostaddr
, (MAI_SERVPORT_STR
*) 0, 0);
603 sa
= SOCK_ADDR_PTR(mask_list
.addrs
+ i
);
604 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa
), SOCK_ADDR_LEN(sa
),
605 &hostmask
, (MAI_SERVPORT_STR
*) 0, 0);
606 vstream_printf("%s/%s\n", hostaddr
.buf
, hostmask
.buf
);
607 vstream_fflush(VSTREAM_OUT
);
609 inet_addr_list_free(&addr_list
);
610 inet_addr_list_free(&mask_list
);