1 #include <sys/cdefs.h> 2 __RCSID("$NetBSD: duid.c,v 1.9 2015/07/09 10:15:34 roy Exp $"); 3 4 /* 5 * dhcpcd - DHCP client daemon 6 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name> 7 * All rights reserved 8 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 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 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #define DUID_TIME_EPOCH 946684800 32 #define DUID_LLT 1 33 #define DUID_LL 3 34 35 #include <sys/socket.h> 36 #include <sys/types.h> 37 38 #include <net/if.h> 39 #include <net/if_arp.h> 40 41 #include <errno.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <time.h> 46 #include <unistd.h> 47 48 #ifndef ARPHRD_NETROM 49 # define ARPHRD_NETROM 0 50 #endif 51 52 #include "common.h" 53 #include "dhcpcd.h" 54 #include "duid.h" 55 56 static size_t 57 duid_make(unsigned char *d, const struct interface *ifp, uint16_t type) 58 { 59 unsigned char *p; 60 uint16_t u16; 61 time_t t; 62 uint32_t u32; 63 64 p = d; 65 u16 = htons(type); 66 memcpy(p, &u16, 2); 67 p += 2; 68 u16 = htons(ifp->family); 69 memcpy(p, &u16, 2); 70 p += 2; 71 if (type == DUID_LLT) { 72 /* time returns seconds from jan 1 1970, but DUID-LLT is 73 * seconds from jan 1 2000 modulo 2^32 */ 74 t = time(NULL) - DUID_TIME_EPOCH; 75 u32 = htonl((uint32_t)t & 0xffffffff); 76 memcpy(p, &u32, 4); 77 p += 4; 78 } 79 /* Finally, add the MAC address of the interface */ 80 memcpy(p, ifp->hwaddr, ifp->hwlen); 81 p += ifp->hwlen; 82 return (size_t)(p - d); 83 } 84 85 #define DUID_STRLEN DUID_LEN * 3 86 static size_t 87 duid_get(unsigned char *d, const struct interface *ifp) 88 { 89 FILE *fp; 90 int x = 0; 91 size_t len = 0; 92 char line[DUID_STRLEN]; 93 const struct interface *ifp2; 94 95 /* If we already have a DUID then use it as it's never supposed 96 * to change once we have one even if the interfaces do */ 97 if ((fp = fopen(DUID, "r"))) { 98 while (fgets(line, DUID_STRLEN, fp)) { 99 len = strlen(line); 100 if (len) { 101 if (line[len - 1] == '\n') 102 line[len - 1] = '\0'; 103 } 104 len = hwaddr_aton(NULL, line); 105 if (len && len <= DUID_LEN) { 106 hwaddr_aton(d, line); 107 break; 108 } 109 len = 0; 110 } 111 fclose(fp); 112 if (len) 113 return len; 114 } else { 115 if (errno != ENOENT) 116 logger(ifp->ctx, LOG_ERR, 117 "error reading DUID: %s: %m", DUID); 118 } 119 120 /* No file? OK, lets make one based on our interface */ 121 if (ifp->family == ARPHRD_NETROM) { 122 logger(ifp->ctx, LOG_WARNING, 123 "%s: is a NET/ROM psuedo interface", ifp->name); 124 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 125 if (ifp2->family != ARPHRD_NETROM) 126 break; 127 } 128 if (ifp2) { 129 ifp = ifp2; 130 logger(ifp->ctx, LOG_WARNING, 131 "picked interface %s to generate a DUID", 132 ifp->name); 133 } else { 134 logger(ifp->ctx, LOG_WARNING, 135 "no interfaces have a fixed hardware address"); 136 return duid_make(d, ifp, DUID_LL); 137 } 138 } 139 140 if (!(fp = fopen(DUID, "w"))) { 141 logger(ifp->ctx, LOG_ERR, "error writing DUID: %s: %m", DUID); 142 return duid_make(d, ifp, DUID_LL); 143 } 144 len = duid_make(d, ifp, DUID_LLT); 145 x = fprintf(fp, "%s\n", hwaddr_ntoa(d, len, line, sizeof(line))); 146 if (fclose(fp) == EOF) 147 x = -1; 148 /* Failed to write the duid? scrub it, we cannot use it */ 149 if (x < 1) { 150 logger(ifp->ctx, LOG_ERR, "error writing DUID: %s: %m", DUID); 151 unlink(DUID); 152 return duid_make(d, ifp, DUID_LL); 153 } 154 return len; 155 } 156 157 size_t duid_init(const struct interface *ifp) 158 { 159 160 if (ifp->ctx->duid == NULL) { 161 ifp->ctx->duid = malloc(DUID_LEN); 162 if (ifp->ctx->duid == NULL) { 163 logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); 164 return 0; 165 } 166 ifp->ctx->duid_len = duid_get(ifp->ctx->duid, ifp); 167 } 168 return ifp->ctx->duid_len; 169 } 170