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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * This module reads and writes the stable identifier values, DUID and IAID.
  29  */
  30 
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <string.h>
  35 #include <limits.h>
  36 #include <fcntl.h>
  37 #include <errno.h>
  38 #include <libdlpi.h>
  39 #include <uuid/uuid.h>
  40 #include <sys/types.h>
  41 #include <sys/stat.h>
  42 #include <net/if.h>
  43 #include <netinet/dhcp6.h>
  44 #include <dhcp_inittab.h>
  45 #include <sys/ethernet.h>
  46 
  47 #define DUID_FILE       "/etc/dhcp/duid"
  48 #define IAID_FILE       "/etc/dhcp/iaid"
  49 
  50 struct iaid_ent {
  51         uint32_t        ie_iaid;
  52         char            ie_name[LIFNAMSIZ];
  53 };
  54 
  55 /*
  56  * read_stable_duid(): read the system's stable DUID, if any
  57  *
  58  *   input: size_t *: pointer to a size_t to return the DUID length
  59  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
  60  *    note: memory returned is from malloc; caller must free.
  61  */
  62 
  63 uchar_t *
  64 read_stable_duid(size_t *duidlen)
  65 {
  66         int fd;
  67         ssize_t retv;
  68         struct stat sb;
  69         uchar_t *duid = NULL;
  70 
  71         if ((fd = open(DUID_FILE, O_RDONLY)) == -1)
  72                 return (NULL);
  73         if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) &&
  74             (duid = malloc(sb.st_size)) != NULL) {
  75                 retv = read(fd, duid, sb.st_size);
  76                 if (retv == sb.st_size) {
  77                         *duidlen = sb.st_size;
  78                 } else {
  79                         free(duid);
  80                         /*
  81                          * Make sure that errno always gets set when something
  82                          * goes wrong.
  83                          */
  84                         if (retv >= 0)
  85                                 errno = EINVAL;
  86                         duid = NULL;
  87                 }
  88         }
  89         (void) close(fd);
  90         return (duid);
  91 }
  92 
  93 /*
  94  * write_stable_duid(): write the system's stable DUID.
  95  *
  96  *   input: const uchar_t *: pointer to the DUID buffer
  97  *          size_t: length of the DUID
  98  *  output: int: 0 on success, -1 on error.  errno is set on error.
  99  */
 100 
 101 int
 102 write_stable_duid(const uchar_t *duid, size_t duidlen)
 103 {
 104         int fd;
 105         ssize_t retv;
 106 
 107         (void) unlink(DUID_FILE);
 108         if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1)
 109                 return (-1);
 110         retv = write(fd, duid, duidlen);
 111         if (retv == duidlen) {
 112                 return (close(fd));
 113         } else {
 114                 (void) close(fd);
 115                 if (retv >= 0)
 116                         errno = ENOSPC;
 117                 return (-1);
 118         }
 119 }
 120 
 121 /*
 122  * make_stable_duid(): create a new DUID
 123  *
 124  *   input: const char *: name of physical interface for reference
 125  *          size_t *: pointer to a size_t to return the DUID length
 126  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
 127  *    note: memory returned is from malloc; caller must free.
 128  */
 129 
 130 uchar_t *
 131 make_stable_duid(const char *physintf, size_t *duidlen)
 132 {
 133         int len;
 134         dlpi_info_t dlinfo;
 135         dlpi_handle_t dh = NULL;
 136         uint_t arptype;
 137         duid_en_t *den;
 138 
 139         /*
 140          * Try to read the MAC layer address for the physical interface
 141          * provided as a hint.  If that works, we can use a DUID-LLT.
 142          */
 143 
 144         if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS &&
 145             dlpi_bind(dh, ETHERTYPE_IPV6, NULL) == DLPI_SUCCESS &&
 146             dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS &&
 147             (len = dlinfo.di_physaddrlen) > 0 &&
 148             (arptype = dlpi_arptype(dlinfo.di_mactype) != 0)) {
 149                 duid_llt_t *dllt;
 150                 time_t now;
 151 
 152                 if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
 153                         dlpi_close(dh);
 154                         return (NULL);
 155                 }
 156 
 157                 (void) memcpy((dllt + 1), dlinfo.di_physaddr, len);
 158                 dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
 159                 dllt->dllt_hwtype = htons(arptype);
 160                 now = time(NULL) - DUID_TIME_BASE;
 161                 dllt->dllt_time = htonl(now);
 162                 *duidlen = sizeof (*dllt) + len;
 163                 dlpi_close(dh);
 164                 return ((uchar_t *)dllt);
 165         }
 166         if (dh != NULL)
 167                 dlpi_close(dh);
 168 
 169         /*
 170          * If we weren't able to create a DUID based on the network interface
 171          * in use, then generate one based on a UUID.
 172          */
 173         den = malloc(sizeof (*den) + UUID_LEN);
 174         if (den != NULL) {
 175                 uuid_t uuid;
 176 
 177                 den->den_dutype = htons(DHCPV6_DUID_EN);
 178                 DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
 179                 uuid_generate(uuid);
 180                 (void) memcpy(den + 1, uuid, UUID_LEN);
 181                 *duidlen = sizeof (*den) + UUID_LEN;
 182         }
 183         return ((uchar_t *)den);
 184 }
 185 
 186 /*
 187  * read_stable_iaid(): read a link's stable IAID, if any
 188  *
 189  *   input: const char *: interface name
 190  *  output: uint32_t: the IAID, or 0 if none
 191  */
 192 
 193 uint32_t
 194 read_stable_iaid(const char *intf)
 195 {
 196         int fd;
 197         struct iaid_ent ie;
 198 
 199         if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
 200                 return (0);
 201         while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
 202                 if (strcmp(intf, ie.ie_name) == 0) {
 203                         (void) close(fd);
 204                         return (ie.ie_iaid);
 205                 }
 206         }
 207         (void) close(fd);
 208         return (0);
 209 }
 210 
 211 /*
 212  * write_stable_iaid(): write out a link's stable IAID
 213  *
 214  *   input: const char *: interface name
 215  *  output: uint32_t: the IAID, or 0 if none
 216  */
 217 
 218 int
 219 write_stable_iaid(const char *intf, uint32_t iaid)
 220 {
 221         int fd;
 222         struct iaid_ent ie;
 223         ssize_t retv;
 224 
 225         if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
 226                 return (0);
 227         while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
 228                 if (strcmp(intf, ie.ie_name) == 0) {
 229                         (void) close(fd);
 230                         if (iaid == ie.ie_iaid) {
 231                                 return (0);
 232                         } else {
 233                                 errno = EINVAL;
 234                                 return (-1);
 235                         }
 236                 }
 237         }
 238         (void) memset(&ie, 0, sizeof (ie));
 239         ie.ie_iaid = iaid;
 240         (void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
 241         retv = write(fd, &ie, sizeof (ie));
 242         (void) close(fd);
 243         if (retv == sizeof (ie)) {
 244                 return (0);
 245         } else {
 246                 if (retv >= 0)
 247                         errno = ENOSPC;
 248                 return (-1);
 249         }
 250 }
 251 
 252 /*
 253  * make_stable_iaid(): create a stable IAID for a link
 254  *
 255  *   input: const char *: interface name
 256  *          uint32_t: the ifIndex for this link (as a "hint")
 257  *  output: uint32_t: the new IAID, never zero
 258  */
 259 
 260 /* ARGSUSED */
 261 uint32_t
 262 make_stable_iaid(const char *intf, uint32_t hint)
 263 {
 264         int fd;
 265         struct iaid_ent ie;
 266         uint32_t maxid, minunused;
 267         boolean_t recheck;
 268 
 269         if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
 270                 return (hint);
 271         maxid = 0;
 272         minunused = 1;
 273         /*
 274          * This logic is deliberately unoptimized.  The reason is that it runs
 275          * essentially just once per interface for the life of the system.
 276          * Once the IAID is established, there's no reason to generate it
 277          * again, and all we care about here is correctness.  Also, IAIDs tend
 278          * to get added in a logical sequence order, so the outer loop should
 279          * not normally run more than twice.
 280          */
 281         do {
 282                 recheck = B_FALSE;
 283                 while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
 284                         if (ie.ie_iaid > maxid)
 285                                 maxid = ie.ie_iaid;
 286                         if (ie.ie_iaid == minunused) {
 287                                 recheck = B_TRUE;
 288                                 minunused++;
 289                         }
 290                         if (ie.ie_iaid == hint)
 291                                 hint = 0;
 292                 }
 293                 if (recheck)
 294                         (void) lseek(fd, 0, SEEK_SET);
 295         } while (recheck);
 296         (void) close(fd);
 297         if (hint != 0)
 298                 return (hint);
 299         else if (maxid != UINT32_MAX)
 300                 return (maxid + 1);
 301         else
 302                 return (minunused);
 303 }