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