Bug 159099 - [ioctl] ioctl SIOCGIFCONF does not export an array of struct ifreq
Summary: [ioctl] ioctl SIOCGIFCONF does not export an array of struct ifreq
Status: Open
Alias: None
Product: Documentation
Classification: Unclassified
Component: Manual Pages (show other bugs)
Version: Latest
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-07-22 07:50 UTC by Sreeram B S
Modified: 2018-04-11 21:27 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sreeram B S 2011-07-22 07:50:08 UTC
Hi,
I have written a program to output interface names. This program uses
the ioctl SIOCGIFCONF. The program compiles successfully, but upon
execution, the output contains valid interface names *and* bland
interface names, as well. The program and its output are as follows:

**********************************************************
bsd213-64# cat ifaddr.c 
/* This is a program to identify the interfaces and their ip-addresses. */

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int sock, temp;
    u_char buf[1024], *ptr;
    struct ifconf ifc;
    struct ifreq *ifr;

    /* Create a UDP socket. */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("Socket");
        exit(-1);
    }
    printf("Created the socket.\n");

    /* Call ioctl() to get the interface information. */
    ifc.ifc_buf = buf;
    ifc.ifc_len = 1024;
    temp = ioctl(sock, SIOCGIFCONF, &ifc);
    if (temp < 0) {
        perror("SIOCIFCONF");
        close(sock);
        exit(-2);
    }

    printf("Got the information of the interfaces.\n");
    printf("Information length: %d\n", ifc.ifc_len);
    
    /* Run through each interface now. */
    for (ptr = ifc.ifc_buf; ptr < buf + ifc.ifc_len;)
    {
        ifr = (struct ifreq *)ptr;
        printf("    %s : \n", ifr->ifr_name);
        ptr = ptr + sizeof(struct ifreq);
    }
    close(sock);
    exit(0);
}

######## Output ##########

bsd213-64# ./ifaddr
Created the socket.
Got the information of the interfaces.
Information length: 368
    bce0 : 
    i : 
     : 
     : 
    bce1 : 
     : 
    8 : 
     : 
     : 
     : 
     : 
     : 
bsd213-64#
******************************************************************

But upon running the same program, absolutely same (without even a single
change), on a Linux machine or a Solaris 10 machine, the output contains
only valid interface names, which is the expected behavior.

*****************************************************************
[sreeramb@sreeramb1-lxp misc]$ uname -o
GNU/Linux
[sreeramb@sreeramb1-lxp misc]$
[sreeramb@sreeramb1-lxp misc]$ gcc ifaddr.c -o ifaddr
[sreeramb@sreeramb1-lxp misc]$ ./ifaddr
Created the socket.
Got the information of the interfaces.
Information length: 96
    lo : 
    eth0 : 
    virbr0 : 
[sreeramb@sreeramb1-lxp misc]$
*****************************************************************

This means that the data returned by the ioctl SIOCGIFCONF is incorrect
or the format is messed up. This needs attention and has to be corrected.

Please note that I understand that there are other methods to obtain the
interfaces and IP addresses like using getipaddrs() system call etc. But
incorrect behavior of SIOCGIFCONF needs attention because:

  1. This leads to portability problems. Any program using this ioctl
     and running well on other OSes cannot be portable on FreeBSD because
     of the problem in the ioctl. 
  2. Moreover, this ioctl is important since almost all network related
     applications use it and it should be intact.

Please provide a fix for this.

Regards,
Sreeram

How-To-Repeat: Please compile and execute the program pasted above.
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2011-07-22 10:34:44 UTC
Responsible Changed
From-To: freebsd-i386->freebsd-bugs

reclassify.
Comment 2 Alexander V. Chernikov 2011-07-25 05:38:12 UTC
Data returned by SIOCGIFCONF is correct. It just has to be read another
way. General struct sockaddr was planned to be enough for holding
AF_INET/AF_INET6 address. It is not enough large to hold AF_LINK address
(sockaddr_dl), for example. There is also traditional 4.3BSD socket
interface with sa_len field contained within struct sockaddr. *BSD, AIX,
 Darwin, QNX are using it. Linux, Solaris, Cygwin does not. This really
leads to portability problems for many people.
On all systems supporting sa_len real ifreq length is calculated using
sa_len field.
sa_len support should be checked before compilation (usually done by
autoconf)

Please see /usr/src/contrib/traceroute/ifaddrlist.c for example of doing
this really portable way.
Comment 3 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:59:28 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped
Comment 4 Brooks Davis freebsd_committer 2018-03-29 22:51:19 UTC
The documented behavior of SIOCGIFCONF is to write an array of struct ifreq.  If all interface addresses (including link layer addresses) fit in struct sockaddr's 14 bytes of sa_data this is true.  If any longer addresses exist (e.g. IPv6 addresses) then the implementation writes out the ifr_name value (16-bytes) followed by the sockaddr.  The next item follows the same rule.

Both the current implementation and tcpdump assume that there is no pad between ifr_name and ifr_addr.  This assumption holds for all current architectures.

[I believe this is a documentation bug in both the comments and the manpages.  Documentation should note the above issues and this this interface is non-portable.]