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