>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     r...@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     r...@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:
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to