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