1/* vi: set sw=4 ts=4: */ 2/* 3 * gen_uuid.c --- generate a DCE-compatible uuid 4 * 5 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. 6 * 7 * %Begin-Header% 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, and the entire permission notice in its entirety, 13 * including the disclaimer of warranties. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of the author may not be used to endorse or promote 18 * products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF 24 * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 31 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH 32 * DAMAGE. 33 * %End-Header% 34 */ 35 36#include <unistd.h> 37#include <stdlib.h> 38#include <string.h> 39#include <fcntl.h> 40#include <errno.h> 41#include <sys/types.h> 42#include <sys/stat.h> 43#include <sys/file.h> 44#include <sys/time.h> 45#ifdef HAVE_SYS_IOCTL_H 46#include <sys/ioctl.h> 47#endif 48#include <sys/socket.h> 49#ifdef HAVE_SYS_SOCKIO_H 50#include <sys/sockio.h> 51#endif 52#ifdef HAVE_NET_IF_H 53#include <net/if.h> 54#endif 55#ifdef HAVE_NETINET_IN_H 56#include <netinet/in.h> 57#endif 58#ifdef HAVE_NET_IF_DL_H 59#include <net/if_dl.h> 60#endif 61 62#include "uuidP.h" 63 64#ifdef HAVE_SRANDOM 65#define srand(x) srandom(x) 66#define rand() random() 67#endif 68 69static int get_random_fd(void) 70{ 71 struct timeval tv; 72 static int fd = -2; 73 int i; 74 75 if (fd == -2) { 76 gettimeofday(&tv, 0); 77 fd = open("/dev/urandom", O_RDONLY); 78 if (fd == -1) 79 fd = open("/dev/random", O_RDONLY | O_NONBLOCK); 80 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); 81 } 82 /* Crank the random number generator a few times */ 83 gettimeofday(&tv, 0); 84 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) 85 rand(); 86 return fd; 87} 88 89 90/* 91 * Generate a series of random bytes. Use /dev/urandom if possible, 92 * and if not, use srandom/random. 93 */ 94static void get_random_bytes(void *buf, int nbytes) 95{ 96 int i, n = nbytes, fd = get_random_fd(); 97 int lose_counter = 0; 98 unsigned char *cp = (unsigned char *) buf; 99 100 if (fd >= 0) { 101 while (n > 0) { 102 i = read(fd, cp, n); 103 if (i <= 0) { 104 if (lose_counter++ > 16) 105 break; 106 continue; 107 } 108 n -= i; 109 cp += i; 110 lose_counter = 0; 111 } 112 } 113 114 /* 115 * We do this all the time, but this is the only source of 116 * randomness if /dev/random/urandom is out to lunch. 117 */ 118 for (cp = buf, i = 0; i < nbytes; i++) 119 *cp++ ^= (rand() >> 7) & 0xFF; 120} 121 122/* 123 * Get the ethernet hardware address, if we can find it... 124 */ 125static int get_node_id(unsigned char *node_id) 126{ 127#ifdef HAVE_NET_IF_H 128 int sd; 129 struct ifreq ifr, *ifrp; 130 struct ifconf ifc; 131 char buf[1024]; 132 int n, i; 133 unsigned char *a; 134#ifdef HAVE_NET_IF_DL_H 135 struct sockaddr_dl *sdlp; 136#endif 137 138/* 139 * BSD 4.4 defines the size of an ifreq to be 140 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len 141 * However, under earlier systems, sa_len isn't present, so the size is 142 * just sizeof(struct ifreq) 143 */ 144#ifdef HAVE_SA_LEN 145#ifndef max 146#define max(a,b) ((a) > (b) ? (a) : (b)) 147#endif 148#define ifreq_size(i) max(sizeof(struct ifreq),\ 149 sizeof((i).ifr_name)+(i).ifr_addr.sa_len) 150#else 151#define ifreq_size(i) sizeof(struct ifreq) 152#endif /* HAVE_SA_LEN*/ 153 154 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 155 if (sd < 0) { 156 return -1; 157 } 158 memset(buf, 0, sizeof(buf)); 159 ifc.ifc_len = sizeof(buf); 160 ifc.ifc_buf = buf; 161 if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { 162 close(sd); 163 return -1; 164 } 165 n = ifc.ifc_len; 166 for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { 167 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); 168 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); 169#ifdef SIOCGIFHWADDR 170 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) 171 continue; 172 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; 173#else 174#ifdef SIOCGENADDR 175 if (ioctl(sd, SIOCGENADDR, &ifr) < 0) 176 continue; 177 a = (unsigned char *) ifr.ifr_enaddr; 178#else 179#ifdef HAVE_NET_IF_DL_H 180 sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; 181 if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) 182 continue; 183 a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; 184#else 185 close(sd); 186 return 0; 187#endif /* HAVE_NET_IF_DL_H */ 188#endif /* SIOCGENADDR */ 189#endif /* SIOCGIFHWADDR */ 190 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) 191 continue; 192 if (node_id) { 193 memcpy(node_id, a, 6); 194 close(sd); 195 return 1; 196 } 197 } 198 close(sd); 199#endif 200 return 0; 201} 202 203/* Assume that the gettimeofday() has microsecond granularity */ 204#define MAX_ADJUSTMENT 10 205 206static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq) 207{ 208 static int adjustment = 0; 209 static struct timeval last = {0, 0}; 210 static uint16_t clock_seq; 211 struct timeval tv; 212 unsigned long long clock_reg; 213 214try_again: 215 gettimeofday(&tv, 0); 216 if ((last.tv_sec == 0) && (last.tv_usec == 0)) { 217 get_random_bytes(&clock_seq, sizeof(clock_seq)); 218 clock_seq &= 0x3FFF; 219 last = tv; 220 last.tv_sec--; 221 } 222 if ((tv.tv_sec < last.tv_sec) || 223 ((tv.tv_sec == last.tv_sec) && 224 (tv.tv_usec < last.tv_usec))) { 225 clock_seq = (clock_seq+1) & 0x3FFF; 226 adjustment = 0; 227 last = tv; 228 } else if ((tv.tv_sec == last.tv_sec) && 229 (tv.tv_usec == last.tv_usec)) { 230 if (adjustment >= MAX_ADJUSTMENT) 231 goto try_again; 232 adjustment++; 233 } else { 234 adjustment = 0; 235 last = tv; 236 } 237 238 clock_reg = tv.tv_usec*10 + adjustment; 239 clock_reg += ((unsigned long long) tv.tv_sec)*10000000; 240 clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000; 241 242 *clock_high = clock_reg >> 32; 243 *clock_low = clock_reg; 244 *ret_clock_seq = clock_seq; 245 return 0; 246} 247 248void uuid_generate_time(uuid_t out) 249{ 250 static unsigned char node_id[6]; 251 static int has_init = 0; 252 struct uuid uu; 253 uint32_t clock_mid; 254 255 if (!has_init) { 256 if (get_node_id(node_id) <= 0) { 257 get_random_bytes(node_id, 6); 258 /* 259 * Set multicast bit, to prevent conflicts 260 * with IEEE 802 addresses obtained from 261 * network cards 262 */ 263 node_id[0] |= 0x01; 264 } 265 has_init = 1; 266 } 267 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq); 268 uu.clock_seq |= 0x8000; 269 uu.time_mid = (uint16_t) clock_mid; 270 uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; 271 memcpy(uu.node, node_id, 6); 272 uuid_pack(&uu, out); 273} 274 275void uuid_generate_random(uuid_t out) 276{ 277 uuid_t buf; 278 struct uuid uu; 279 280 get_random_bytes(buf, sizeof(buf)); 281 uuid_unpack(buf, &uu); 282 283 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; 284 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; 285 uuid_pack(&uu, out); 286} 287 288/* 289 * This is the generic front-end to uuid_generate_random and 290 * uuid_generate_time. It uses uuid_generate_random only if 291 * /dev/urandom is available, since otherwise we won't have 292 * high-quality randomness. 293 */ 294void uuid_generate(uuid_t out) 295{ 296 if (get_random_fd() >= 0) 297 uuid_generate_random(out); 298 else 299 uuid_generate_time(out); 300} 301