1 /* $OpenBSD: uuid.c,v 1.5 2016/08/27 01:42:37 guenther Exp $ */ 2 /* 3 * Copyright (c) 2002, Stockholms Universitet 4 * (Stockholm University, Stockholm Sweden) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 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 * 18 * 3. Neither the name of the university nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * NCS/DCE/AFS/GUID generator 37 * 38 * for more information about DCE UUID, see 39 * <http://www.opengroup.org/onlinepubs/9629399/apdxa.htm> 40 * 41 * Note, the Microsoft GUID is a DCE UUID, but it seems like they 42 * folded in the seq num with the node part. That would explain how 43 * the reserved field have a bit pattern 110 when reserved is a 2 bit 44 * field. 45 * 46 * XXX should hash the node address for privacy issues 47 */ 48 49 #include <sys/types.h> 50 #include <sys/socket.h> 51 #include <sys/time.h> 52 #include <netinet/in.h> 53 #include <net/if.h> 54 #include <net/if_types.h> 55 #include <net/if_dl.h> 56 #include <sys/file.h> 57 58 #include <fcntl.h> 59 #include <ifaddrs.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 65 #include "uuid.h" 66 67 static uint32_t seq_num; 68 static struct timeval last_time; 69 static int32_t counter; 70 static char nodeaddr[6]; 71 72 enum { UUID_NODE_MULTICAST = 0x80 }; 73 74 static int 75 time_cmp(struct timeval *tv1, struct timeval *tv2) 76 { 77 if (tv1->tv_sec > tv2->tv_sec) 78 return -1; 79 if (tv1->tv_sec < tv2->tv_sec) 80 return 1; 81 if (tv1->tv_usec > tv2->tv_usec) 82 return -1; 83 if (tv1->tv_usec < tv2->tv_usec) 84 return 1; 85 return 0; 86 } 87 88 static void 89 get_node_addr(char *addr) 90 { 91 struct ifaddrs *ifa, *ifa0; 92 int found_mac = 0; 93 94 if (getifaddrs(&ifa0) != 0) 95 ifa0 = NULL; 96 97 for (ifa = ifa0; ifa != NULL && !found_mac; ifa = ifa->ifa_next) { 98 if (ifa->ifa_addr == NULL) 99 continue; 100 101 #if IFF_LOOPBACK 102 if (ifa->ifa_flags & IFF_LOOPBACK) 103 continue; 104 #endif 105 106 switch (ifa->ifa_addr->sa_family) { 107 #ifdef AF_LINK 108 case AF_LINK: { 109 struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr; 110 111 switch (dl->sdl_type) { 112 case IFT_ETHER: 113 case IFT_FDDI: 114 if (dl->sdl_alen == 6) { 115 memcpy(addr, LLADDR(dl), 6); 116 found_mac = 1; 117 } 118 } 119 120 } 121 #endif 122 default: 123 break; 124 } 125 } 126 127 if (ifa0 != NULL) 128 freeifaddrs(ifa0); 129 130 if (!found_mac) { 131 /* 132 * Set the multicast bit to make sure we won't collide with an 133 * allocated (mac) address. 134 */ 135 arc4random_buf(addr, 6); 136 addr[0] |= UUID_NODE_MULTICAST; 137 } 138 return; 139 } 140 141 /* 142 * Creates a new UUID. 143 */ 144 145 void 146 uuid_create(afsUUID *uuid) 147 { 148 static int uuid_inited = 0; 149 struct timeval tv; 150 int ret, got_time; 151 uint64_t dce_time; 152 153 if (uuid_inited == 0) { 154 gettimeofday(&last_time, NULL); 155 seq_num = arc4random(); 156 get_node_addr(nodeaddr); 157 uuid_inited = 1; 158 } 159 160 gettimeofday(&tv, NULL); 161 162 got_time = 0; 163 164 do { 165 ret = time_cmp(&tv, &last_time); 166 if (ret < 0) { 167 /* Time went backward, just inc seq_num and be done. 168 * seq_num is 6 + 8 bit field it the uuid, so let it wrap 169 * around. don't let it be zero. 170 */ 171 seq_num = (seq_num + 1) & 0x3fff ; 172 if (seq_num == 0) 173 seq_num++; 174 got_time = 1; 175 counter = 0; 176 last_time = tv; 177 } else if (ret > 0) { 178 /* time went forward, reset counter and be happy */ 179 last_time = tv; 180 counter = 0; 181 got_time = 1; 182 } else { 183 #define UUID_MAX_HZ (1) /* make this bigger fix you have larger tickrate */ 184 #define MULTIPLIER_100_NANO_SEC 10 185 if (++counter < UUID_MAX_HZ * MULTIPLIER_100_NANO_SEC) 186 got_time = 1; 187 } 188 } while(!got_time); 189 190 /* 191 * now shift time to dce_time, epoch 00:00:00:00, 15 October 1582 192 * dce time ends year ~3400, so start to worry now 193 */ 194 195 dce_time = tv.tv_usec * MULTIPLIER_100_NANO_SEC + counter; 196 dce_time += ((uint64_t)tv.tv_sec) * 10000000; 197 dce_time += (((uint64_t)0x01b21dd2) << 32) + 0x13814000; 198 199 uuid->time_low = dce_time & 0xffffffff; 200 uuid->time_mid = 0xffff & (dce_time >> 32); 201 uuid->time_hi_and_version = 0x0fff & (dce_time >> 48); 202 203 uuid->time_hi_and_version |= (1 << 12); 204 205 uuid->clock_seq_low = seq_num & 0xff; 206 uuid->clock_seq_hi_and_reserved = (seq_num >> 8) & 0x3f; 207 uuid->clock_seq_hi_and_reserved |= 0x80; /* dce variant */ 208 209 memcpy(uuid->node, nodeaddr, 6); 210 } 211 212 /* 213 * Converts a UUID from binary representation to a string representation. 214 */ 215 216 void 217 uuid_to_string(const afsUUID *uuid, char *str, size_t strsz) 218 { 219 snprintf(str, strsz, 220 "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 221 uuid->time_low, 222 uuid->time_mid, 223 uuid->time_hi_and_version, 224 (unsigned char)uuid->clock_seq_hi_and_reserved, 225 (unsigned char)uuid->clock_seq_low, 226 (unsigned char)uuid->node[0], 227 (unsigned char)uuid->node[1], 228 (unsigned char)uuid->node[2], 229 (unsigned char)uuid->node[3], 230 (unsigned char)uuid->node[4], 231 (unsigned char)uuid->node[5]); 232 } 233 234 235 #ifdef TEST 236 int 237 main(int argc, char **argv) 238 { 239 char str[1000]; 240 afsUUID u1, u2; 241 242 uuid_create(&u1); 243 244 uuid_to_string(&u1, str, sizeof(str)); 245 246 printf("u: %s\n", str); 247 248 if (uuid_from_string(str, &u2)) { 249 printf("failed to parse\n"); 250 return 0; 251 } 252 253 if (bcmp(&u1, &u2, sizeof(u1)) != 0) 254 printf("u1 != u2\n"); 255 256 return 0; 257 } 258 #endif 259