1 /* $OpenBSD: uuid.c,v 1.6 2018/04/26 12:42:51 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
57 #include <fcntl.h>
58 #include <ifaddrs.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63
64 #include "uuid.h"
65
66 static uint32_t seq_num;
67 static struct timeval last_time;
68 static int32_t counter;
69 static char nodeaddr[6];
70
71 enum { UUID_NODE_MULTICAST = 0x80 };
72
73 static int
time_cmp(struct timeval * tv1,struct timeval * tv2)74 time_cmp(struct timeval *tv1, struct timeval *tv2)
75 {
76 if (tv1->tv_sec > tv2->tv_sec)
77 return -1;
78 if (tv1->tv_sec < tv2->tv_sec)
79 return 1;
80 if (tv1->tv_usec > tv2->tv_usec)
81 return -1;
82 if (tv1->tv_usec < tv2->tv_usec)
83 return 1;
84 return 0;
85 }
86
87 static void
get_node_addr(char * addr)88 get_node_addr(char *addr)
89 {
90 struct ifaddrs *ifa, *ifa0;
91 int found_mac = 0;
92
93 if (getifaddrs(&ifa0) != 0)
94 ifa0 = NULL;
95
96 for (ifa = ifa0; ifa != NULL && !found_mac; ifa = ifa->ifa_next) {
97 if (ifa->ifa_addr == NULL)
98 continue;
99
100 #if IFF_LOOPBACK
101 if (ifa->ifa_flags & IFF_LOOPBACK)
102 continue;
103 #endif
104
105 switch (ifa->ifa_addr->sa_family) {
106 #ifdef AF_LINK
107 case AF_LINK: {
108 struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr;
109
110 switch (dl->sdl_type) {
111 case IFT_ETHER:
112 case IFT_FDDI:
113 if (dl->sdl_alen == 6) {
114 memcpy(addr, LLADDR(dl), 6);
115 found_mac = 1;
116 }
117 }
118
119 }
120 #endif
121 default:
122 break;
123 }
124 }
125
126 if (ifa0 != NULL)
127 freeifaddrs(ifa0);
128
129 if (!found_mac) {
130 /*
131 * Set the multicast bit to make sure we won't collide with an
132 * allocated (mac) address.
133 */
134 arc4random_buf(addr, 6);
135 addr[0] |= UUID_NODE_MULTICAST;
136 }
137 return;
138 }
139
140 /*
141 * Creates a new UUID.
142 */
143
144 void
uuid_create(afsUUID * uuid)145 uuid_create(afsUUID *uuid)
146 {
147 static int uuid_inited = 0;
148 struct timeval tv;
149 int ret, got_time;
150 uint64_t dce_time;
151
152 if (uuid_inited == 0) {
153 gettimeofday(&last_time, NULL);
154 seq_num = arc4random();
155 get_node_addr(nodeaddr);
156 uuid_inited = 1;
157 }
158
159 gettimeofday(&tv, NULL);
160
161 got_time = 0;
162
163 do {
164 ret = time_cmp(&tv, &last_time);
165 if (ret < 0) {
166 /* Time went backward, just inc seq_num and be done.
167 * seq_num is 6 + 8 bit field it the uuid, so let it wrap
168 * around. don't let it be zero.
169 */
170 seq_num = (seq_num + 1) & 0x3fff ;
171 if (seq_num == 0)
172 seq_num++;
173 got_time = 1;
174 counter = 0;
175 last_time = tv;
176 } else if (ret > 0) {
177 /* time went forward, reset counter and be happy */
178 last_time = tv;
179 counter = 0;
180 got_time = 1;
181 } else {
182 #define UUID_MAX_HZ (1) /* make this bigger fix you have larger tickrate */
183 #define MULTIPLIER_100_NANO_SEC 10
184 if (++counter < UUID_MAX_HZ * MULTIPLIER_100_NANO_SEC)
185 got_time = 1;
186 }
187 } while(!got_time);
188
189 /*
190 * now shift time to dce_time, epoch 00:00:00:00, 15 October 1582
191 * dce time ends year ~3400, so start to worry now
192 */
193
194 dce_time = tv.tv_usec * MULTIPLIER_100_NANO_SEC + counter;
195 dce_time += ((uint64_t)tv.tv_sec) * 10000000;
196 dce_time += (((uint64_t)0x01b21dd2) << 32) + 0x13814000;
197
198 uuid->time_low = dce_time & 0xffffffff;
199 uuid->time_mid = 0xffff & (dce_time >> 32);
200 uuid->time_hi_and_version = 0x0fff & (dce_time >> 48);
201
202 uuid->time_hi_and_version |= (1 << 12);
203
204 uuid->clock_seq_low = seq_num & 0xff;
205 uuid->clock_seq_hi_and_reserved = (seq_num >> 8) & 0x3f;
206 uuid->clock_seq_hi_and_reserved |= 0x80; /* dce variant */
207
208 memcpy(uuid->node, nodeaddr, 6);
209 }
210
211 /*
212 * Converts a UUID from binary representation to a string representation.
213 */
214
215 void
uuid_to_string(const afsUUID * uuid,char * str,size_t strsz)216 uuid_to_string(const afsUUID *uuid, char *str, size_t strsz)
217 {
218 snprintf(str, strsz,
219 "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
220 uuid->time_low,
221 uuid->time_mid,
222 uuid->time_hi_and_version,
223 (unsigned char)uuid->clock_seq_hi_and_reserved,
224 (unsigned char)uuid->clock_seq_low,
225 (unsigned char)uuid->node[0],
226 (unsigned char)uuid->node[1],
227 (unsigned char)uuid->node[2],
228 (unsigned char)uuid->node[3],
229 (unsigned char)uuid->node[4],
230 (unsigned char)uuid->node[5]);
231 }
232
233
234 #ifdef TEST
235 int
main(int argc,char ** argv)236 main(int argc, char **argv)
237 {
238 char str[1000];
239 afsUUID u1, u2;
240
241 uuid_create(&u1);
242
243 uuid_to_string(&u1, str, sizeof(str));
244
245 printf("u: %s\n", str);
246
247 if (uuid_from_string(str, &u2)) {
248 printf("failed to parse\n");
249 return 0;
250 }
251
252 if (bcmp(&u1, &u2, sizeof(u1)) != 0)
253 printf("u1 != u2\n");
254
255 return 0;
256 }
257 #endif
258