1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <netdb.h>
  27 #include <nss_dbdefs.h>
  28 #include <netinet/in.h>
  29 #include <sys/socket.h>
  30 #include <string.h>
  31 #include <stdio.h>
  32 #include <sys/sockio.h>
  33 #include <sys/types.h>
  34 #include <stdlib.h>
  35 #include <net/if.h>
  36 #include <ifaddrs.h>
  37 #include <libsocket_priv.h>
  38 
  39 /*
  40  * Create a linked list of `struct ifaddrs' structures, one for each
  41  * address that is UP. If successful, store the list in *ifap and
  42  * return 0.  On errors, return -1 and set `errno'.
  43  *
  44  * The storage returned in *ifap is allocated dynamically and can
  45  * only be properly freed by passing it to `freeifaddrs'.
  46  */
  47 int
  48 getifaddrs(struct ifaddrs **ifap)
  49 {
  50         int             err;
  51         char            *cp;
  52         struct ifaddrs  *curr;
  53 
  54         if (ifap == NULL) {
  55                 errno = EINVAL;
  56                 return (-1);
  57         }
  58         *ifap = NULL;
  59         err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
  60         if (err == 0) {
  61                 for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
  62                         if ((cp = strchr(curr->ifa_name, ':')) != NULL)
  63                                 *cp = '\0';
  64                 }
  65         }
  66         return (err);
  67 }
  68 
  69 void
  70 freeifaddrs(struct ifaddrs *ifa)
  71 {
  72         struct ifaddrs *curr;
  73 
  74         while (ifa != NULL) {
  75                 curr = ifa;
  76                 ifa = ifa->ifa_next;
  77                 free(curr->ifa_name);
  78                 free(curr->ifa_addr);
  79                 free(curr->ifa_netmask);
  80                 free(curr->ifa_dstaddr);
  81                 free(curr);
  82         }
  83 }
  84 
  85 /*
  86  * Returns all addresses configured on the system. If flags contain
  87  * LIFC_ENABLED, only the addresses that are UP are returned.
  88  * Address list that is returned by this function must be freed
  89  * using freeifaddrs().
  90  */
  91 int
  92 getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
  93 {
  94         struct lifreq *buf = NULL;
  95         struct lifreq *lifrp;
  96         struct lifreq lifrl;
  97         int ret;
  98         int s, n, numifs;
  99         struct ifaddrs *curr, *prev;
 100         sa_family_t lifr_af;
 101         int sock4;
 102         int sock6;
 103         int err;
 104 
 105         if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 106                 return (-1);
 107         if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
 108                 err = errno;
 109                 close(sock4);
 110                 errno = err;
 111                 return (-1);
 112         }
 113 
 114 retry:
 115         /* Get all interfaces from SIOCGLIFCONF */
 116         ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
 117         if (ret != 0)
 118                 goto fail;
 119 
 120         /*
 121          * Loop through the interfaces obtained from SIOCGLIFCOMF
 122          * and retrieve the addresses, netmask and flags.
 123          */
 124         prev = NULL;
 125         lifrp = buf;
 126         *ifap = NULL;
 127         for (n = 0; n < numifs; n++, lifrp++) {
 128 
 129                 /* Prepare for the ioctl call */
 130                 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
 131                     sizeof (lifrl.lifr_name));
 132                 lifr_af = lifrp->lifr_addr.ss_family;
 133                 if (af != AF_UNSPEC && lifr_af != af)
 134                         continue;
 135 
 136                 s = (lifr_af == AF_INET ? sock4 : sock6);
 137 
 138                 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
 139                         goto fail;
 140                 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
 141                         continue;
 142 
 143                 /*
 144                  * Allocate the current list node. Each node contains data
 145                  * for one ifaddrs structure.
 146                  */
 147                 curr = calloc(1, sizeof (struct ifaddrs));
 148                 if (curr == NULL)
 149                         goto fail;
 150 
 151                 if (prev != NULL) {
 152                         prev->ifa_next = curr;
 153                 } else {
 154                         /* First node in the linked list */
 155                         *ifap = curr;
 156                 }
 157                 prev = curr;
 158 
 159                 curr->ifa_flags = lifrl.lifr_flags;
 160                 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
 161                         goto fail;
 162 
 163                 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
 164                 if (curr->ifa_addr == NULL)
 165                         goto fail;
 166                 (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
 167                     sizeof (struct sockaddr_storage));
 168 
 169                 /* Get the netmask */
 170                 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
 171                         goto fail;
 172                 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
 173                 if (curr->ifa_netmask == NULL)
 174                         goto fail;
 175                 (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
 176                     sizeof (struct sockaddr_storage));
 177 
 178                 /* Get the destination for a pt-pt interface */
 179                 if (curr->ifa_flags & IFF_POINTOPOINT) {
 180                         if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
 181                                 goto fail;
 182                         curr->ifa_dstaddr = malloc(
 183                             sizeof (struct sockaddr_storage));
 184                         if (curr->ifa_dstaddr == NULL)
 185                                 goto fail;
 186                         (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
 187                             sizeof (struct sockaddr_storage));
 188                 } else if (curr->ifa_flags & IFF_BROADCAST) {
 189                         if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
 190                                 goto fail;
 191                         curr->ifa_broadaddr = malloc(
 192                             sizeof (struct sockaddr_storage));
 193                         if (curr->ifa_broadaddr == NULL)
 194                                 goto fail;
 195                         (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
 196                             sizeof (struct sockaddr_storage));
 197                 }
 198 
 199         }
 200         free(buf);
 201         close(sock4);
 202         close(sock6);
 203         return (0);
 204 fail:
 205         err = errno;
 206         free(buf);
 207         freeifaddrs(*ifap);
 208         *ifap = NULL;
 209         if (err == ENXIO)
 210                 goto retry;
 211         close(sock4);
 212         close(sock6);
 213         errno = err;
 214         return (-1);
 215 }
 216 
 217 /*
 218  * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
 219  */
 220 int
 221 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
 222     int64_t lifc_flags)
 223 {
 224         struct lifnum lifn;
 225         struct lifconf lifc;
 226         size_t bufsize;
 227         char *tmp;
 228         caddr_t *buf = (caddr_t *)lifr;
 229 
 230         lifn.lifn_family = af;
 231         lifn.lifn_flags = lifc_flags;
 232 
 233         *buf = NULL;
 234 retry:
 235         if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
 236                 goto fail;
 237 
 238         /*
 239          * When calculating the buffer size needed, add a small number
 240          * of interfaces to those we counted.  We do this to capture
 241          * the interface status of potential interfaces which may have
 242          * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
 243          */
 244         bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
 245 
 246         if ((tmp = realloc(*buf, bufsize)) == NULL)
 247                 goto fail;
 248 
 249         *buf = tmp;
 250         lifc.lifc_family = af;
 251         lifc.lifc_flags = lifc_flags;
 252         lifc.lifc_len = bufsize;
 253         lifc.lifc_buf = *buf;
 254         if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
 255                 goto fail;
 256 
 257         *numifs = lifc.lifc_len / sizeof (struct lifreq);
 258         if (*numifs >= (lifn.lifn_count + 4)) {
 259                 /*
 260                  * If every entry was filled, there are probably
 261                  * more interfaces than (lifn.lifn_count + 4).
 262                  * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
 263                  * get all the interfaces.
 264                  */
 265                 goto retry;
 266         }
 267         return (0);
 268 fail:
 269         free(*buf);
 270         *buf = NULL;
 271         return (-1);
 272 }