root/test/ifconf/ifconf.c
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

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

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define NADDRS          128

/* XXX _SIZEOF_ADDR_IFREQ should accept ptr */
#define _SIZEOF_ADDR_IFREQ1(ifr) \
        ((ifr)->ifr_addr.sa_len > sizeof(struct sockaddr) ? \
         (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
          (ifr)->ifr_addr.sa_len) : sizeof(struct ifreq))

static void
usage(const char *cmd)
{
        fprintf(stderr, "%s [-n naddrs]\n", cmd);
        exit(1);
}

int
main(int argc, char *argv[])
{
        struct ifconf ifc;
        struct ifreq *ifr;
        caddr_t pos;
        int s, naddrs, opt;

        naddrs = NADDRS;

        while ((opt = getopt(argc, argv, "n:")) != -1) {
                switch (opt) {
                case 'n':
                        naddrs = strtol(optarg, NULL, 10);
                        break;
                default:
                        usage(argv[0]);
                }
        }
        if (naddrs <= 0)
                usage(argv[0]);

        s = socket(AF_INET, SOCK_DGRAM, 0);
        if (s < 0)
                err(2, "socket failed");

        memset(&ifc, 0, sizeof(ifc));
        ifc.ifc_len = sizeof(struct sockaddr_storage) * naddrs;
        ifc.ifc_buf = malloc(ifc.ifc_len);
        if (ifc.ifc_buf == NULL)
                err(2, "malloc failed");

        if (ioctl(s, SIOCGIFCONF, &ifc, sizeof(ifc)) < 0)
                err(2, "ioctl failed");

        pos = ifc.ifc_buf;
        while (ifc.ifc_len >= (int)sizeof(*ifr)) {
                int len;

                ifr = (struct ifreq *)pos;
                len = _SIZEOF_ADDR_IFREQ1(ifr);
                if (ifc.ifc_len < len)
                        err(2, "invalid ifc.ifc_len");

                if (ifr->ifr_addr.sa_family == AF_UNSPEC) {
                        printf("%s: no address\n", ifr->ifr_name);
                } else if (ifr->ifr_addr.sa_family == AF_INET ||
                    ifr->ifr_addr.sa_family == AF_INET6) {
                        char addr_str[INET6_ADDRSTRLEN];
                        const void *src;
                        const char *ret;

                        if (ifr->ifr_addr.sa_family == AF_INET) {
                                const struct sockaddr_in *in =
                                    (const struct sockaddr_in *)&ifr->ifr_addr;
                                src = &in->sin_addr;
                        } else {
                                const struct sockaddr_in6 *in6 =
                                    (const struct sockaddr_in6 *)&ifr->ifr_addr;
                                src = &in6->sin6_addr;
                        }

                        ret = inet_ntop(ifr->ifr_addr.sa_family, src,
                            addr_str, sizeof(addr_str));
                        if (ret == NULL)
                                err(2, "inet_ntop failed");
                        printf("%s: inet%c %s\n", ifr->ifr_name,
                            ifr->ifr_addr.sa_family == AF_INET ? '4' : '6',
                            ret);
                } else if (ifr->ifr_addr.sa_family == AF_LINK) {
                        const struct sockaddr_dl *dl =
                            (const struct sockaddr_dl *)&ifr->ifr_addr;

                        printf("%s: link%d\n", ifr->ifr_name, dl->sdl_index);
                } else {
                        printf("%s: unknown family %d\n", ifr->ifr_name,
                            ifr->ifr_addr.sa_family);
                }

                ifc.ifc_len -= len;
                pos += len;
        }
        if (ifc.ifc_len != 0)
                printf("ifc_len left %d\n", ifc.ifc_len);

        exit(0);
}