Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 8 Nov 2010 06:50:31 GMT
From:      Kelly Yancey <kbyanc@gmail.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/152036: getifaddrs(3) returns truncated sockaddrs for netmasks
Message-ID:  <201011080650.oA86oVQL089520@www.freebsd.org>
Resent-Message-ID: <201011080700.oA870HW1099968@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         152036
>Category:       kern
>Synopsis:       getifaddrs(3) returns truncated sockaddrs for netmasks
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Nov 08 07:00:17 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator:     Kelly Yancey
>Release:        6.4, 8.1
>Organization:
>Environment:
FreeBSD gateway.posi.net 6.4-STABLE FreeBSD 6.4-STABLE #0: Thu Jan  7 13:48:25 JST 2010     root@gateway.posi.net:/usr/obj/usr/src/sys/GATEWAY  i386

-- AND --

FreeBSD gateway.posi.net 8.1-RELEASE FreeBSD 8.1-RELEASE #0: Mon Jul 19 02:55:53 UTC 2010     root@almeida.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
The getifaddrs(3) function returns a pointer to a sockaddr for each of the interface's address, netmask, broadcast address, and destination address (for peer-to-peer links).  However, the sockaddr for IPv4 netmasks is frequently truncated to less than a full sockaddr_in.  In fact, it is typically truncated in the middle of the sin_addr field.

The sockaddr_in structures appear to be truncated to the minimum number of bytes necessary to hold the netmask.  For example, if the netmask was 255.255.254.0, then the sockaddr is truncated to just 7 bytes, with the sin_addr field consisting of just 3 bytes: 0xff, 0xff, 0xfe.

In other words, the sockaddr_in structure isn't just truncated for IPv4 netmasks, it is truncated in the middle of a primitive type (in this case, an uint32_t).

>How-To-Repeat:
Attached is a simple program that fetches and prints all of the interface addresses using the getifaddrs(3) API.  You will notice that the netmasks associated with the IPv4 addresses are all truncated to less than a full sockaddr_in.
>Fix:


Patch attached with submission follows:

/*
 *
 *
 */

#include <sys/types.h>
#include <sys/socket.h>

#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>

#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>

static void	 print_addr(const struct sockaddr *sa);

void
print_addr(const struct sockaddr *sa)
{
	char buffer[100];
	const struct sockaddr_in *sin;
	const struct sockaddr_in6 *sin6;
	const struct sockaddr_dl *sdl;
	int i;

	if (sa == NULL) {
		printf("NULL");
		return;
	}

	printf("(family: %d, len:%d", sa->sa_family, sa->sa_len);

#define	SIZECHECK(type) \
	if (sa->sa_len < sizeof(type)) { \
		printf(", TRUNCATED -- should be %zu bytes", sizeof(type)); \
		break; \
	}

	switch (sa->sa_family) {
	case AF_INET:
		SIZECHECK(struct sockaddr_in)
		sin = (const struct sockaddr_in *)sa;
		inet_ntop(AF_INET, &sin->sin_addr, buffer, sizeof(buffer));
		printf("addr:%s", buffer);
		break;
	case AF_INET6:
		SIZECHECK(struct sockaddr_in6)
		sin6 = (const struct sockaddr_in6 *)sa;
		inet_ntop(AF_INET6, &sin6->sin6_addr, buffer, sizeof(buffer));
		printf(", addr:%s", buffer);
		break;
	case AF_LINK:
		SIZECHECK(struct sockaddr_dl)
		sdl = (const struct sockaddr_dl *)sa;
		printf(", addr:%s", buffer);
		/*
		 * Could use link_ntoa() but its output format is a little
		 * unusual so print the address manually.
		 */ 
		for (i = 0; i < sdl->sdl_alen; i++) {
			if (i > 0)
				printf(":");
			printf("%02x", sdl->sdl_data[sdl->sdl_nlen + i]);
		}
		break;
	default:
		/* nothing */
		break;
	}

#undef SIZECHECK

	printf(")");
}

int
main(int ac __unused, char **av __unused)
{
	struct ifaddrs *addrlist, *addr;

	getifaddrs(&addrlist);

	for (addr = addrlist; addr != NULL; addr = addr->ifa_next) {
		printf("%s:\n", addr->ifa_name);
		printf("\tifa_addr = ");
		print_addr(addr->ifa_addr);
		printf("\n");

		printf("\tifa_netmask = ");
		print_addr(addr->ifa_netmask);
		printf("\n");

		printf("\tdstaddr = ");
		print_addr(addr->ifa_dstaddr);
		printf("\n\n");
	}

	freeifaddrs(addrlist);
	return 0;
}


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201011080650.oA86oVQL089520>