18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI * dhcpcd - DHCP client daemon
4*80aa9461SRoy Marples * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI * All rights reserved
67827cba2SAaron LI
77827cba2SAaron LI * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI * modification, are permitted provided that the following conditions
97827cba2SAaron LI * are met:
107827cba2SAaron LI * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI * notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI * notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI * documentation and/or other materials provided with the distribution.
157827cba2SAaron LI *
167827cba2SAaron LI * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI * SUCH DAMAGE.
277827cba2SAaron LI */
287827cba2SAaron LI
297827cba2SAaron LI #define UUID_LEN 36
307827cba2SAaron LI #define DUID_TIME_EPOCH 946684800
317827cba2SAaron LI
327827cba2SAaron LI #include <sys/param.h>
337827cba2SAaron LI #include <sys/socket.h>
347827cba2SAaron LI #include <sys/types.h>
357827cba2SAaron LI #ifdef BSD
367827cba2SAaron LI # include <sys/sysctl.h>
377827cba2SAaron LI #endif
387827cba2SAaron LI
397827cba2SAaron LI #include <arpa/inet.h>
407827cba2SAaron LI
417827cba2SAaron LI #include <net/if.h>
427827cba2SAaron LI #include <net/if_arp.h>
437827cba2SAaron LI
447827cba2SAaron LI #include <errno.h>
457827cba2SAaron LI #include <stdint.h>
467827cba2SAaron LI #include <stdio.h>
477827cba2SAaron LI #include <stdlib.h>
487827cba2SAaron LI #include <string.h>
497827cba2SAaron LI #include <time.h>
507827cba2SAaron LI #include <unistd.h>
517827cba2SAaron LI
527827cba2SAaron LI #include "common.h"
537827cba2SAaron LI #include "dhcpcd.h"
547827cba2SAaron LI #include "duid.h"
557827cba2SAaron LI #include "logerr.h"
567827cba2SAaron LI
57*80aa9461SRoy Marples /*
58*80aa9461SRoy Marples * Machine, system or product UUIDs are not guaranteed unique.
59*80aa9461SRoy Marples * Let's not use them by default.
60*80aa9461SRoy Marples */
61*80aa9461SRoy Marples #ifdef USE_MACHINE_UUID
627827cba2SAaron LI static size_t
duid_machineuuid(char * uuid,size_t uuid_len)637827cba2SAaron LI duid_machineuuid(char *uuid, size_t uuid_len)
647827cba2SAaron LI {
657827cba2SAaron LI int r;
667827cba2SAaron LI size_t len = uuid_len;
677827cba2SAaron LI
687827cba2SAaron LI #if defined(HW_UUID) /* OpenBSD */
697827cba2SAaron LI int mib[] = { CTL_HW, HW_UUID };
707827cba2SAaron LI
717827cba2SAaron LI r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
727827cba2SAaron LI #elif defined(KERN_HOSTUUID) /* FreeBSD */
737827cba2SAaron LI int mib[] = { CTL_KERN, KERN_HOSTUUID };
747827cba2SAaron LI
757827cba2SAaron LI r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
767827cba2SAaron LI #elif defined(__NetBSD__)
777827cba2SAaron LI r = sysctlbyname("machdep.dmi.system-uuid", uuid, &len, NULL, 0);
787827cba2SAaron LI #elif defined(__linux__)
797827cba2SAaron LI FILE *fp;
807827cba2SAaron LI
817827cba2SAaron LI fp = fopen("/sys/class/dmi/id/product_uuid", "r");
827827cba2SAaron LI if (fp == NULL)
837827cba2SAaron LI return 0;
847827cba2SAaron LI if (fgets(uuid, (int)uuid_len, fp) == NULL) {
857827cba2SAaron LI fclose(fp);
867827cba2SAaron LI return 0;
877827cba2SAaron LI }
887827cba2SAaron LI len = strlen(uuid) + 1;
897827cba2SAaron LI fclose(fp);
908d36e1dfSRoy Marples r = len == 1 ? -1 : 0;
917827cba2SAaron LI #else
928d36e1dfSRoy Marples UNUSED(uuid);
937827cba2SAaron LI r = -1;
947827cba2SAaron LI errno = ENOSYS;
957827cba2SAaron LI #endif
967827cba2SAaron LI
977827cba2SAaron LI if (r == -1)
987827cba2SAaron LI return 0;
997827cba2SAaron LI return len;
1007827cba2SAaron LI }
1017827cba2SAaron LI
1027827cba2SAaron LI static size_t
duid_make_uuid(uint8_t * d)1037827cba2SAaron LI duid_make_uuid(uint8_t *d)
1047827cba2SAaron LI {
1057827cba2SAaron LI uint16_t type = htons(DUID_UUID);
1067827cba2SAaron LI char uuid[UUID_LEN + 1];
1077827cba2SAaron LI size_t l;
1087827cba2SAaron LI
1097827cba2SAaron LI if (duid_machineuuid(uuid, sizeof(uuid)) != sizeof(uuid))
1107827cba2SAaron LI return 0;
1117827cba2SAaron LI
1127827cba2SAaron LI /* All zeros UUID is not valid */
1137827cba2SAaron LI if (strcmp("00000000-0000-0000-0000-000000000000", uuid) == 0)
1147827cba2SAaron LI return 0;
1157827cba2SAaron LI
1167827cba2SAaron LI memcpy(d, &type, sizeof(type));
1177827cba2SAaron LI l = sizeof(type);
1187827cba2SAaron LI d += sizeof(type);
1197827cba2SAaron LI l += hwaddr_aton(d, uuid);
1207827cba2SAaron LI return l;
1217827cba2SAaron LI }
122*80aa9461SRoy Marples #endif
1237827cba2SAaron LI
1246e63cc1fSRoy Marples size_t
duid_make(void * d,const struct interface * ifp,uint16_t type)1256e63cc1fSRoy Marples duid_make(void *d, const struct interface *ifp, uint16_t type)
1267827cba2SAaron LI {
1277827cba2SAaron LI uint8_t *p;
1287827cba2SAaron LI uint16_t u16;
1297827cba2SAaron LI time_t t;
1307827cba2SAaron LI uint32_t u32;
1317827cba2SAaron LI
1326e63cc1fSRoy Marples if (ifp->hwlen == 0)
1336e63cc1fSRoy Marples return 0;
1346e63cc1fSRoy Marples
1357827cba2SAaron LI p = d;
1367827cba2SAaron LI u16 = htons(type);
1376e63cc1fSRoy Marples memcpy(p, &u16, sizeof(u16));
1386e63cc1fSRoy Marples p += sizeof(u16);
139d4fb1e02SRoy Marples u16 = htons(ifp->hwtype);
1406e63cc1fSRoy Marples memcpy(p, &u16, sizeof(u16));
1416e63cc1fSRoy Marples p += sizeof(u16);
1427827cba2SAaron LI if (type == DUID_LLT) {
1437827cba2SAaron LI /* time returns seconds from jan 1 1970, but DUID-LLT is
1447827cba2SAaron LI * seconds from jan 1 2000 modulo 2^32 */
1457827cba2SAaron LI t = time(NULL) - DUID_TIME_EPOCH;
1467827cba2SAaron LI u32 = htonl((uint32_t)t & 0xffffffff);
1476e63cc1fSRoy Marples memcpy(p, &u32, sizeof(u32));
1486e63cc1fSRoy Marples p += sizeof(u32);
1497827cba2SAaron LI }
1507827cba2SAaron LI /* Finally, add the MAC address of the interface */
1517827cba2SAaron LI memcpy(p, ifp->hwaddr, ifp->hwlen);
1527827cba2SAaron LI p += ifp->hwlen;
1536e63cc1fSRoy Marples return (size_t)(p - (uint8_t *)d);
1547827cba2SAaron LI }
1557827cba2SAaron LI
1567827cba2SAaron LI #define DUID_STRLEN DUID_LEN * 3
1577827cba2SAaron LI static size_t
duid_get(struct dhcpcd_ctx * ctx,const struct interface * ifp)158d4fb1e02SRoy Marples duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
1597827cba2SAaron LI {
1607827cba2SAaron LI uint8_t *data;
161d4fb1e02SRoy Marples size_t len, slen;
1627827cba2SAaron LI char line[DUID_STRLEN];
1637827cba2SAaron LI const struct interface *ifp2;
1647827cba2SAaron LI
1657827cba2SAaron LI /* If we already have a DUID then use it as it's never supposed
1667827cba2SAaron LI * to change once we have one even if the interfaces do */
167d4fb1e02SRoy Marples if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {
1687827cba2SAaron LI if (len <= DUID_LEN) {
169d4fb1e02SRoy Marples ctx->duid = data;
1707827cba2SAaron LI return len;
1717827cba2SAaron LI }
1727827cba2SAaron LI logerrx("DUID too big (max %u): %s", DUID_LEN, DUID);
1737827cba2SAaron LI /* Keep the buffer, will assign below. */
1747827cba2SAaron LI } else {
1757827cba2SAaron LI if (errno != ENOENT)
1767827cba2SAaron LI logerr("%s", DUID);
1777827cba2SAaron LI if ((data = malloc(DUID_LEN)) == NULL) {
1787827cba2SAaron LI logerr(__func__);
1797827cba2SAaron LI return 0;
1807827cba2SAaron LI }
1817827cba2SAaron LI }
1827827cba2SAaron LI
1837827cba2SAaron LI /* No file? OK, lets make one based the machines UUID */
184d4fb1e02SRoy Marples if (ifp == NULL) {
185*80aa9461SRoy Marples #ifdef USE_MACHINE_UUID
186a0d9933aSRoy Marples if (ctx->duid_type != DUID_DEFAULT &&
187a0d9933aSRoy Marples ctx->duid_type != DUID_UUID)
18893ddca5eSRoy Marples len = 0;
18993ddca5eSRoy Marples else
1907827cba2SAaron LI len = duid_make_uuid(data);
191d4fb1e02SRoy Marples if (len == 0)
192d4fb1e02SRoy Marples free(data);
193d4fb1e02SRoy Marples else
194d4fb1e02SRoy Marples ctx->duid = data;
1957827cba2SAaron LI return len;
196*80aa9461SRoy Marples #else
197*80aa9461SRoy Marples free(data);
198*80aa9461SRoy Marples return 0;
199*80aa9461SRoy Marples #endif
200d4fb1e02SRoy Marples }
201d4fb1e02SRoy Marples
202d4fb1e02SRoy Marples /* Regardless of what happens we will create a DUID to use. */
203d4fb1e02SRoy Marples ctx->duid = data;
2047827cba2SAaron LI
2057827cba2SAaron LI /* No UUID? OK, lets make one based on our interface */
2068d36e1dfSRoy Marples if (ifp->hwlen == 0) {
2078d36e1dfSRoy Marples logwarnx("%s: does not have hardware address", ifp->name);
2087827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
2098d36e1dfSRoy Marples if (ifp2->hwlen != 0)
2107827cba2SAaron LI break;
2117827cba2SAaron LI }
2127827cba2SAaron LI if (ifp2) {
2137827cba2SAaron LI ifp = ifp2;
2147827cba2SAaron LI logwarnx("picked interface %s to generate a DUID",
2157827cba2SAaron LI ifp->name);
2167827cba2SAaron LI } else {
217a0d9933aSRoy Marples if (ctx->duid_type != DUID_LL)
2187827cba2SAaron LI logwarnx("no interfaces have a fixed hardware "
2197827cba2SAaron LI "address");
2207827cba2SAaron LI return duid_make(data, ifp, DUID_LL);
2217827cba2SAaron LI }
2227827cba2SAaron LI }
2237827cba2SAaron LI
224a0d9933aSRoy Marples len = duid_make(data, ifp,
225a0d9933aSRoy Marples ctx->duid_type == DUID_LL ? DUID_LL : DUID_LLT);
226d4fb1e02SRoy Marples hwaddr_ntoa(data, len, line, sizeof(line));
227d4fb1e02SRoy Marples slen = strlen(line);
228d4fb1e02SRoy Marples if (slen < sizeof(line) - 2) {
229d4fb1e02SRoy Marples line[slen++] = '\n';
230d4fb1e02SRoy Marples line[slen] = '\0';
231d4fb1e02SRoy Marples }
232d4fb1e02SRoy Marples if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {
233d4fb1e02SRoy Marples logerr("%s: cannot write duid", __func__);
234a0d9933aSRoy Marples if (ctx->duid_type != DUID_LL)
2357827cba2SAaron LI return duid_make(data, ifp, DUID_LL);
2367827cba2SAaron LI }
2377827cba2SAaron LI return len;
2387827cba2SAaron LI }
2397827cba2SAaron LI
240d4fb1e02SRoy Marples size_t
duid_init(struct dhcpcd_ctx * ctx,const struct interface * ifp)241d4fb1e02SRoy Marples duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)
2427827cba2SAaron LI {
2437827cba2SAaron LI
244d4fb1e02SRoy Marples if (ctx->duid == NULL)
245d4fb1e02SRoy Marples ctx->duid_len = duid_get(ctx, ifp);
246d4fb1e02SRoy Marples return ctx->duid_len;
2477827cba2SAaron LI }
248