#include <sys/param.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sbuf.h>
#include <sys/socket.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
#include <sys/jail.h>
#include <sys/uuid.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/vnet.h>
CTASSERT(sizeof(struct uuid) == 16);
struct uuid_private {
union {
uint64_t ll;
struct {
uint32_t low;
uint16_t mid;
uint16_t hi;
} x;
} time;
uint16_t seq;
uint16_t node[UUID_NODE_LEN>>1];
};
CTASSERT(sizeof(struct uuid_private) == 16);
struct uuid_macaddr {
uint16_t state;
#define UUID_ETHER_EMPTY 0
#define UUID_ETHER_RANDOM 1
#define UUID_ETHER_UNIQUE 2
uint16_t node[UUID_NODE_LEN>>1];
};
static struct uuid_private uuid_last;
#define UUID_NETHER 4
static struct uuid_macaddr uuid_ether[UUID_NETHER];
static struct mtx uuid_mutex;
MTX_SYSINIT(uuid_lock, &uuid_mutex, "UUID generator mutex lock", MTX_DEF);
static void
uuid_node(uint16_t *node)
{
int i;
if (uuid_ether[0].state == UUID_ETHER_EMPTY) {
for (i = 0; i < (UUID_NODE_LEN>>1); i++)
uuid_ether[0].node[i] = (uint16_t)arc4random();
*((uint8_t*)uuid_ether[0].node) |= 0x01;
uuid_ether[0].state = UUID_ETHER_RANDOM;
}
for (i = 0; i < (UUID_NODE_LEN>>1); i++)
node[i] = uuid_ether[0].node[i];
}
static uint64_t
uuid_time(void)
{
struct bintime bt;
uint64_t time = 0x01B21DD213814000LL;
bintime(&bt);
time += (uint64_t)bt.sec * 10000000LL;
time += (10000000LL * (uint32_t)(bt.frac >> 32)) >> 32;
return (time & ((1LL << 60) - 1LL));
}
struct uuid *
kern_uuidgen(struct uuid *store, size_t count)
{
struct uuid_private uuid;
uint64_t time;
size_t n;
mtx_lock(&uuid_mutex);
uuid_node(uuid.node);
time = uuid_time();
if (uuid_last.time.ll == 0LL || uuid_last.node[0] != uuid.node[0] ||
uuid_last.node[1] != uuid.node[1] ||
uuid_last.node[2] != uuid.node[2])
uuid.seq = (uint16_t)arc4random() & 0x3fff;
else if (uuid_last.time.ll >= time)
uuid.seq = (uuid_last.seq + 1) & 0x3fff;
else
uuid.seq = uuid_last.seq;
uuid_last = uuid;
uuid_last.time.ll = (time + count - 1) & ((1LL << 60) - 1LL);
mtx_unlock(&uuid_mutex);
uuid.seq = htobe16(uuid.seq | 0x8000);
for (n = 0; n < count; n++) {
uuid.time.x.low = (uint32_t)time;
uuid.time.x.mid = (uint16_t)(time >> 32);
uuid.time.x.hi = ((uint16_t)(time >> 48) & 0xfff) | (1 << 12);
store[n] = *(struct uuid *)&uuid;
time++;
}
return (store);
}
#ifndef _SYS_SYSPROTO_H_
struct uuidgen_args {
struct uuid *store;
int count;
};
#endif
int
sys_uuidgen(struct thread *td, struct uuidgen_args *uap)
{
struct uuid *store;
size_t count;
int error;
if (uap->count < 1 || uap->count > UUIDGEN_BATCH_MAX)
return (EINVAL);
count = uap->count;
store = malloc(count * sizeof(struct uuid), M_TEMP, M_WAITOK);
kern_uuidgen(store, count);
error = copyout(store, uap->store, count * sizeof(struct uuid));
free(store, M_TEMP);
return (error);
}
int
uuid_ether_add(const uint8_t *addr)
{
int i, sum;
if (addr[0] & 0x03)
return (EINVAL);
sum = 0;
for (i = 0; i < UUID_NODE_LEN; i++)
sum += addr[i];
if (sum == 0)
return (EINVAL);
mtx_lock(&uuid_mutex);
i = 0;
while (i < UUID_NETHER && uuid_ether[i].state == UUID_ETHER_UNIQUE) {
if (!bcmp(addr, uuid_ether[i].node, UUID_NODE_LEN)) {
mtx_unlock(&uuid_mutex);
return (EEXIST);
}
i++;
}
if (i == UUID_NETHER) {
mtx_unlock(&uuid_mutex);
return (ENOSPC);
}
if (uuid_ether[i].state == UUID_ETHER_RANDOM && i < UUID_NETHER - 1)
uuid_ether[i + 1] = uuid_ether[i];
uuid_ether[i].state = UUID_ETHER_UNIQUE;
bcopy(addr, uuid_ether[i].node, UUID_NODE_LEN);
mtx_unlock(&uuid_mutex);
return (0);
}
int
uuid_ether_del(const uint8_t *addr)
{
int i;
mtx_lock(&uuid_mutex);
i = 0;
while (i < UUID_NETHER && uuid_ether[i].state == UUID_ETHER_UNIQUE &&
bcmp(addr, uuid_ether[i].node, UUID_NODE_LEN))
i++;
if (i == UUID_NETHER || uuid_ether[i].state != UUID_ETHER_UNIQUE) {
mtx_unlock(&uuid_mutex);
return (ENOENT);
}
while (i < UUID_NETHER - 1 && uuid_ether[i].state != UUID_ETHER_EMPTY) {
uuid_ether[i] = uuid_ether[i + 1];
i++;
}
if (uuid_ether[i].state != UUID_ETHER_EMPTY) {
uuid_ether[i].state = UUID_ETHER_EMPTY;
bzero(uuid_ether[i].node, UUID_NODE_LEN);
}
mtx_unlock(&uuid_mutex);
return (0);
}
int
snprintf_uuid(char *buf, size_t sz, struct uuid *uuid)
{
struct uuid_private *id;
int cnt;
id = (struct uuid_private *)uuid;
cnt = snprintf(buf, sz, "%08x-%04x-%04x-%04x-%04x%04x%04x",
id->time.x.low, id->time.x.mid, id->time.x.hi, be16toh(id->seq),
be16toh(id->node[0]), be16toh(id->node[1]), be16toh(id->node[2]));
return (cnt);
}
int
printf_uuid(struct uuid *uuid)
{
char buf[38];
snprintf_uuid(buf, sizeof(buf), uuid);
return (printf("%s", buf));
}
int
sbuf_printf_uuid(struct sbuf *sb, struct uuid *uuid)
{
char buf[38];
snprintf_uuid(buf, sizeof(buf), uuid);
return (sbuf_cat(sb, buf));
}
void
le_uuid_enc(void *buf, struct uuid const *uuid)
{
u_char *p;
int i;
p = buf;
le32enc(p, uuid->time_low);
le16enc(p + 4, uuid->time_mid);
le16enc(p + 6, uuid->time_hi_and_version);
p[8] = uuid->clock_seq_hi_and_reserved;
p[9] = uuid->clock_seq_low;
for (i = 0; i < _UUID_NODE_LEN; i++)
p[10 + i] = uuid->node[i];
}
void
le_uuid_dec(void const *buf, struct uuid *uuid)
{
u_char const *p;
int i;
p = buf;
uuid->time_low = le32dec(p);
uuid->time_mid = le16dec(p + 4);
uuid->time_hi_and_version = le16dec(p + 6);
uuid->clock_seq_hi_and_reserved = p[8];
uuid->clock_seq_low = p[9];
for (i = 0; i < _UUID_NODE_LEN; i++)
uuid->node[i] = p[10 + i];
}
void
be_uuid_enc(void *buf, struct uuid const *uuid)
{
u_char *p;
int i;
p = buf;
be32enc(p, uuid->time_low);
be16enc(p + 4, uuid->time_mid);
be16enc(p + 6, uuid->time_hi_and_version);
p[8] = uuid->clock_seq_hi_and_reserved;
p[9] = uuid->clock_seq_low;
for (i = 0; i < _UUID_NODE_LEN; i++)
p[10 + i] = uuid->node[i];
}
void
be_uuid_dec(void const *buf, struct uuid *uuid)
{
u_char const *p;
int i;
p = buf;
uuid->time_low = be32dec(p);
uuid->time_mid = be16dec(p + 4);
uuid->time_hi_and_version = be16dec(p + 6);
uuid->clock_seq_hi_and_reserved = p[8];
uuid->clock_seq_low = p[9];
for (i = 0; i < _UUID_NODE_LEN; i++)
uuid->node[i] = p[10 + i];
}
int
validate_uuid(const char *str, size_t size, struct uuid *uuid, int flags)
{
u_int c[11];
int n;
if (size == 0 || *str == '\0') {
if ((flags & VUUIDF_EMPTYOK) != 0) {
if (uuid != NULL)
bzero(uuid, sizeof(*uuid));
return (0);
}
return (EINVAL);
}
if (size != 36)
return (EINVAL);
if (str[8] != '-')
return (EINVAL);
n = sscanf(str, "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x", c + 0, c + 1,
c + 2, c + 3, c + 4, c + 5, c + 6, c + 7, c + 8, c + 9, c + 10);
if (n != 11)
return (EINVAL);
if (uuid != NULL) {
uuid->time_low = c[0];
uuid->time_mid = c[1];
uuid->time_hi_and_version = c[2];
uuid->clock_seq_hi_and_reserved = c[3];
uuid->clock_seq_low = c[4];
for (n = 0; n < 6; n++)
uuid->node[n] = c[n + 5];
}
if ((flags & VUUIDF_CHECKSEMANTICS) == 0)
return (0);
return (((c[3] & 0x80) != 0x00 &&
(c[3] & 0xc0) != 0x80 &&
(c[3] & 0xe0) != 0xc0) ? EINVAL : 0);
}
#define VUUIDF_PARSEFLAGS (VUUIDF_EMPTYOK | VUUIDF_CHECKSEMANTICS)
int
parse_uuid(const char *str, struct uuid *uuid)
{
return (validate_uuid(str, strlen(str), uuid, VUUIDF_PARSEFLAGS));
}
int
uuidcmp(const struct uuid *uuid1, const struct uuid *uuid2)
{
return (memcmp(uuid1, uuid2, sizeof(struct uuid)));
}