2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <netdb.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <netinet/in.h>
2N/A#include <sys/socket.h>
2N/A#include <string.h>
2N/A#include <stdio.h>
2N/A#include <sys/sockio.h>
2N/A#include <sys/types.h>
2N/A#include <stdlib.h>
2N/A#include <net/if.h>
2N/A#include <ifaddrs.h>
2N/A#include <libsocket_priv.h>
2N/A
2N/A/*
2N/A * Create a linked list of `struct ifaddrs' structures, one for each
2N/A * address that is UP. If successful, store the list in *ifap and
2N/A * return 0. On errors, return -1 and set `errno'.
2N/A *
2N/A * The storage returned in *ifap is allocated dynamically and can
2N/A * only be properly freed by passing it to `freeifaddrs'.
2N/A */
2N/Aint
2N/Agetifaddrs(struct ifaddrs **ifap)
2N/A{
2N/A int err;
2N/A char *cp;
2N/A struct ifaddrs *curr;
2N/A
2N/A if (ifap == NULL) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A *ifap = NULL;
2N/A err = getallifaddrs(AF_UNSPEC, ifap, 0, IA_UP_ADDRS_ONLY);
2N/A if (err == 0) {
2N/A for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
2N/A if ((cp = strchr(curr->ifa_name, ':')) != NULL)
2N/A *cp = '\0';
2N/A }
2N/A }
2N/A return (err);
2N/A}
2N/A
2N/Avoid
2N/Afreeifaddrs(struct ifaddrs *ifa)
2N/A{
2N/A struct ifaddrs *curr;
2N/A
2N/A while (ifa != NULL) {
2N/A curr = ifa;
2N/A ifa = ifa->ifa_next;
2N/A free(curr->ifa_name);
2N/A free(curr->ifa_addr);
2N/A free(curr->ifa_netmask);
2N/A free(curr->ifa_dstaddr);
2N/A free(curr);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Returns all addresses configured on the system. If `IA_UP_ADDRS_ONLY'
2N/A * is set in `ia_flags', only the addresses that are IFF_UP are returned.
2N/A * Address list that is returned by this function must be freed
2N/A * using freeifaddrs().
2N/A */
2N/Aint
2N/Agetallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t lifc_flags,
2N/A uint32_t ia_flags)
2N/A{
2N/A struct lifreq *buf = NULL;
2N/A struct lifreq *lifrp;
2N/A struct lifreq lifrl;
2N/A int ret;
2N/A int s, n, numifs;
2N/A struct ifaddrs *curr, *prev;
2N/A sa_family_t lifr_af;
2N/A int sock4;
2N/A int sock6;
2N/A int err;
2N/A
2N/A if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
2N/A return (-1);
2N/A if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
2N/A err = errno;
2N/A (void) close(sock4);
2N/A errno = err;
2N/A return (-1);
2N/A }
2N/A
2N/Aretry:
2N/A /* Get all interfaces from SIOCGLIFCONF */
2N/A ret = getallifs(sock4, af, &buf, &numifs, lifc_flags);
2N/A if (ret != 0)
2N/A goto fail;
2N/A
2N/A /*
2N/A * Loop through the interfaces obtained from SIOCGLIFCOMF
2N/A * and retrieve the addresses, netmask and flags.
2N/A */
2N/A prev = NULL;
2N/A lifrp = buf;
2N/A *ifap = NULL;
2N/A for (n = 0; n < numifs; n++, lifrp++) {
2N/A
2N/A /* Prepare for the ioctl call */
2N/A (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
2N/A sizeof (lifrl.lifr_name));
2N/A lifr_af = lifrp->lifr_addr.ss_family;
2N/A if (af != AF_UNSPEC && lifr_af != af)
2N/A continue;
2N/A
2N/A s = (lifr_af == AF_INET ? sock4 : sock6);
2N/A
2N/A if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
2N/A goto fail;
2N/A if ((ia_flags & IA_UP_ADDRS_ONLY) &&
2N/A !(lifrl.lifr_flags & IFF_UP))
2N/A continue;
2N/A
2N/A /*
2N/A * Allocate the current list node. Each node contains data
2N/A * for one ifaddrs structure.
2N/A */
2N/A curr = calloc(1, sizeof (struct ifaddrs));
2N/A if (curr == NULL)
2N/A goto fail;
2N/A
2N/A if (prev != NULL) {
2N/A prev->ifa_next = curr;
2N/A } else {
2N/A /* First node in the linked list */
2N/A *ifap = curr;
2N/A }
2N/A prev = curr;
2N/A
2N/A curr->ifa_flags = lifrl.lifr_flags;
2N/A if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
2N/A goto fail;
2N/A
2N/A curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
2N/A if (curr->ifa_addr == NULL)
2N/A goto fail;
2N/A (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
2N/A sizeof (struct sockaddr_storage));
2N/A
2N/A /* Get the netmask */
2N/A if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
2N/A goto fail;
2N/A curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
2N/A if (curr->ifa_netmask == NULL)
2N/A goto fail;
2N/A (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
2N/A sizeof (struct sockaddr_storage));
2N/A
2N/A /* Get the destination for a pt-pt interface */
2N/A if (curr->ifa_flags & IFF_POINTOPOINT) {
2N/A if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
2N/A goto fail;
2N/A curr->ifa_dstaddr = malloc(
2N/A sizeof (struct sockaddr_storage));
2N/A if (curr->ifa_dstaddr == NULL)
2N/A goto fail;
2N/A (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
2N/A sizeof (struct sockaddr_storage));
2N/A } else if (curr->ifa_flags & IFF_BROADCAST) {
2N/A if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
2N/A goto fail;
2N/A curr->ifa_broadaddr = malloc(
2N/A sizeof (struct sockaddr_storage));
2N/A if (curr->ifa_broadaddr == NULL)
2N/A goto fail;
2N/A (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
2N/A sizeof (struct sockaddr_storage));
2N/A }
2N/A
2N/A }
2N/A free(buf);
2N/A (void) close(sock4);
2N/A (void) close(sock6);
2N/A return (0);
2N/Afail:
2N/A err = errno;
2N/A free(buf);
2N/A freeifaddrs(*ifap);
2N/A *ifap = NULL;
2N/A if (err == ENXIO)
2N/A goto retry;
2N/A (void) close(sock4);
2N/A (void) close(sock6);
2N/A errno = err;
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
2N/A */
2N/Aint
2N/Agetallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
2N/A int64_t lifc_flags)
2N/A{
2N/A struct lifnum lifn;
2N/A struct lifconf lifc;
2N/A size_t bufsize;
2N/A char *tmp;
2N/A caddr_t *buf = (caddr_t *)lifr;
2N/A
2N/A lifn.lifn_family = af;
2N/A lifn.lifn_flags = lifc_flags;
2N/A
2N/A *buf = NULL;
2N/Aretry:
2N/A if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
2N/A goto fail;
2N/A
2N/A /*
2N/A * When calculating the buffer size needed, add a small number
2N/A * of interfaces to those we counted. We do this to capture
2N/A * the interface status of potential interfaces which may have
2N/A * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
2N/A */
2N/A bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
2N/A
2N/A if ((tmp = realloc(*buf, bufsize)) == NULL)
2N/A goto fail;
2N/A
2N/A *buf = tmp;
2N/A lifc.lifc_family = af;
2N/A lifc.lifc_flags = lifc_flags;
2N/A lifc.lifc_len = bufsize;
2N/A lifc.lifc_buf = *buf;
2N/A if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
2N/A goto fail;
2N/A
2N/A *numifs = lifc.lifc_len / sizeof (struct lifreq);
2N/A if (*numifs >= (lifn.lifn_count + 4)) {
2N/A /*
2N/A * If every entry was filled, there are probably
2N/A * more interfaces than (lifn.lifn_count + 4).
2N/A * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
2N/A * get all the interfaces.
2N/A */
2N/A goto retry;
2N/A }
2N/A return (0);
2N/Afail:
2N/A free(*buf);
2N/A *buf = NULL;
2N/A return (-1);
2N/A}