How to create an UUID (Version 1)
- Posted by jmduro Sep 25, 2018
- 3953 views
Here is my code to generate an universally unique identifier (UUID) version 1 that conforms to RFC4122. It needs OE4.1 on Linux, libpcap0.8 (to detect main network interface).
include std/math.e include std/datetime.e include std/os.e include std/dll.e include std/machine.e include std/socket.e -- UUID -- -- A UUID is 128 bit long, and consists of a 60-bit time value, a 16-bit sequence number and a 48-bit node identifier. -- -- <time>-<sequence>-<mac address> -- 60 16 48 -- <time> number of 100-nanosecond intervals since midnight 15 October 1582 Coordinated Universal Time (UTC), the date on which the Gregorian calendar was first adopted. -- -- In its canonical textual representation, the sixteen octets of a UUID are represented as 32 hexadecimal (base 16) digits, displayed in five groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 alphanumeric characters and four hyphens). For example: -- -- 123e4567-e89b-12d3-a456-426655440000 -- xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx -- -- The four bits of digit M indicate the UUID version, and the one to three most significant bits of digit N indicate the UUID variant. In the example, M is 1 and N is a (10xx), meaning that the UUID is a variant 1, version 1 UUID; that is, a time-based DCE/RFC 4122 UUID. -- -- The canonical 8-4-4-4-12 format string is based on the "record layout" for the 16 bytes of the UUID:[2] -- -- UUID record layout -- -- Name Length Length Contents -- (bytes) (hex digits) -- time_low 4 8 integer giving the low 32 bits of the time -- time_mid 2 4 integer giving the middle 16 bits of the time -- time_hi_and_version 2 4 4-bit "version" in the most significant bits, followed by the high 12 bits of the time -- clock_seq_hi_and_res clock_seq_low 2 4 1-3 bit "variant" in the most significant bits, followed by the 13-15 bit clock sequence -- node 6 12 the 48-bit node id ------------------------------------------------------------------------------ public constant NORMAL = 1, REVERSE = 2 constant PCAP_ERRBUF_SIZE = 256 constant libc = open_dll("") atom pcap = -1 ifdef UNIX then sequence un = uname() sequence arch = un[5] -- following lines work for Ubuntu derivatives -- with other Linux distributions please update corresponding lines if equal(arch, "i686") then pcap = open_dll("/usr/lib/i386-linux-gnu/libpcap.so") elsif equal(arch, "x86_64") then pcap = open_dll("/usr/lib/x86_64-linux-gnu/libpcap.so") elsif equal(arch, "armv7l") then pcap = open_dll("/usr/lib/arm-linux-gnueabihf/libpcap.so.0.8") end if end ifdef if pcap = -1 then puts(1,"Could not open libpcap library!\n") abort(0) end if constant xpcap_lookupdev = define_c_func(pcap, "pcap_lookupdev", {C_POINTER}, C_POINTER), ioctl_ = define_c_func(libc, "ioctl",{C_INT,C_INT,C_INT},C_INT), socket_ = define_c_func(libc, "socket",{C_INT,C_INT,C_INT},C_INT) ------------------------------------------------------------------------------ function hex_string(sequence s) sequence result = "" for i = 1 to length(s) do result &= sprintf("%02x", s[i]) end for return result end function ------------------------------------------------------------------------------- function uint_to_bytes(atom n, integer digits, integer direction=REVERSE) sequence s = repeat(0, digits) if direction = NORMAL then for i = digits to 1 by -1 do s[i] = remainder(n, #100) n = floor(n / #100) end for elsif direction = REVERSE then for i = 1 to digits do s[i] = remainder(n, #100) n = floor(n / #100) end for end if return s end function ------------------------------------------------------------------------------ function get_iface_hw_addr(sequence iface_name) sequence hw_addr atom ifreq, sockid, status hw_addr = "" ifreq = allocate(200) sockid = c_func(socket_,{AF_INET,SOCK_DGRAM,0}) if sockid < 0 then free(ifreq) return {} end if poke(ifreq,iface_name&0) status = c_func(ioctl_,{sockid,#8927,ifreq}) -- HW Address if status >= 0 then hw_addr = "" for ctr = 18 to 23 do hw_addr = hw_addr & sprintf("%02x",peek(ifreq+ctr)) end for hw_addr = hw_addr[1..length(hw_addr)] end if return hw_addr end function ------------------------------------------------------------------------------ function pcap_lookupdev() atom error_buffer = allocate(PCAP_ERRBUF_SIZE) sequence ret atom device = c_func(xpcap_lookupdev, {error_buffer}) if device = NULL then ret = {0, peek_string(error_buffer)} end if free(error_buffer) ret = {1, peek_string(device)} return ret end function ------------------------------------------------------------------------------ function gregorian_time() sequence dt = now_gmt() sequence from = new(1582,10,15, 0, 0, 0) atom nb = trunc(diff(from, dt)*10_000_000) return nb end function ------------------------------------------------------------------------------ function create_uuid() sequence hw_addr, iface_name integer ok sequence uuid = "" sequence s = uint_to_bytes(gregorian_time(), 8, NORMAL) sequence gt = hex_string(s) sequence time_low = tail(gt, 8) gt = gt[1..$-8] sequence time_mid = tail(gt, 4) gt = gt[1..$-4] sequence time_hi = tail(gt, 3) sequence dt = now() sequence midnight = dt[1..3] & {0,0,0} s = uint_to_bytes(remainder(diff(midnight, dt),3600), 2, NORMAL) sequence clock_seq = hex_string(s) integer n = clock_seq[1] - #30 clock_seq[1] = #30 + (8 + and_bits(n, #3)) {ok, iface_name} = pcap_lookupdev() if ok then hw_addr = get_iface_hw_addr(iface_name) else puts(2, "No interface found!\n") end if uuid = time_low & "-" & time_mid & "-1" & time_hi & "-" & clock_seq & "-" & hw_addr return uuid end function ------------------------------------------------------------------------------ sequence s = create_uuid() puts(1, s & "\n")
libpcap cant be forgotten if you specify the default interface as a parameter to create_uuid().
Regards
Jean-Marc