1 /* $OpenBSD: sync.c,v 1.23 2017/02/13 23:04:05 krw Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Bob Beck <beck@openbsd.org> 5 * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/ioctl.h> 22 #include <sys/queue.h> 23 #include <sys/socket.h> 24 25 #include <net/if.h> 26 27 #include <arpa/inet.h> 28 29 #include <netinet/in.h> 30 31 #include <openssl/hmac.h> 32 33 #include <errno.h> 34 #include <netdb.h> 35 #include <sha1.h> 36 #include <string.h> 37 #include <syslog.h> 38 #include <unistd.h> 39 40 #include "dhcp.h" 41 #include "tree.h" 42 #include "dhcpd.h" 43 #include "log.h" 44 #include "sync.h" 45 46 int sync_debug; 47 48 u_int32_t sync_counter; 49 int syncfd = -1; 50 int sendmcast; 51 52 struct sockaddr_in sync_in; 53 struct sockaddr_in sync_out; 54 static char *sync_key; 55 56 struct sync_host { 57 LIST_ENTRY(sync_host) h_entry; 58 59 char *h_name; 60 struct sockaddr_in sh_addr; 61 }; 62 LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts); 63 64 void sync_send(struct iovec *, int); 65 66 int 67 sync_addhost(const char *name, u_short port) 68 { 69 struct addrinfo hints, *res, *res0; 70 struct sync_host *shost; 71 struct sockaddr_in *addr = NULL; 72 73 memset(&hints, 0, sizeof(hints)); 74 hints.ai_family = PF_UNSPEC; 75 hints.ai_socktype = SOCK_STREAM; 76 if (getaddrinfo(name, NULL, &hints, &res0) != 0) 77 return (EINVAL); 78 for (res = res0; res != NULL; res = res->ai_next) { 79 if (addr == NULL && res->ai_family == AF_INET) { 80 addr = (struct sockaddr_in *)res->ai_addr; 81 break; 82 } 83 } 84 if (addr == NULL) { 85 freeaddrinfo(res0); 86 return (EINVAL); 87 } 88 if ((shost = (struct sync_host *) 89 calloc(1, sizeof(struct sync_host))) == NULL) { 90 freeaddrinfo(res0); 91 return (ENOMEM); 92 } 93 shost->h_name = strdup(name); 94 if (shost->h_name == NULL) { 95 free(shost); 96 freeaddrinfo(res0); 97 return (ENOMEM); 98 } 99 100 shost->sh_addr.sin_family = AF_INET; 101 shost->sh_addr.sin_port = htons(port); 102 shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr; 103 freeaddrinfo(res0); 104 105 LIST_INSERT_HEAD(&sync_hosts, shost, h_entry); 106 107 if (sync_debug) 108 log_info("added dhcp sync host %s (address %s, port %d)\n", 109 shost->h_name, inet_ntoa(shost->sh_addr.sin_addr), port); 110 111 return (0); 112 } 113 114 int 115 sync_init(const char *iface, const char *baddr, u_short port) 116 { 117 int one = 1; 118 u_int8_t ttl; 119 struct ifreq ifr; 120 struct ip_mreq mreq; 121 struct sockaddr_in *addr; 122 char ifnam[IFNAMSIZ], *ttlstr; 123 const char *errstr; 124 struct in_addr ina; 125 126 if (iface != NULL) 127 sendmcast++; 128 129 memset(&ina, 0, sizeof(ina)); 130 if (baddr != NULL) { 131 if (inet_pton(AF_INET, baddr, &ina) != 1) { 132 ina.s_addr = htonl(INADDR_ANY); 133 if (iface == NULL) 134 iface = baddr; 135 else if (iface != NULL && strcmp(baddr, iface) != 0) { 136 fprintf(stderr, "multicast interface does " 137 "not match"); 138 return (-1); 139 } 140 } 141 } 142 143 sync_key = SHA1File(DHCP_SYNC_KEY, NULL); 144 if (sync_key == NULL) { 145 if (errno != ENOENT) { 146 log_warn("failed to open sync key"); 147 return (-1); 148 } 149 /* Use empty key by default */ 150 sync_key = ""; 151 } 152 153 syncfd = socket(AF_INET, SOCK_DGRAM, 0); 154 if (syncfd == -1) 155 return (-1); 156 157 if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one, 158 sizeof(one)) == -1) 159 goto fail; 160 161 memset(&sync_out, 0, sizeof(sync_out)); 162 sync_out.sin_family = AF_INET; 163 sync_out.sin_len = sizeof(sync_out); 164 sync_out.sin_addr.s_addr = ina.s_addr; 165 if (baddr == NULL && iface == NULL) 166 sync_out.sin_port = 0; 167 else 168 sync_out.sin_port = htons(port); 169 170 if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1) 171 goto fail; 172 173 /* Don't use multicast messages */ 174 if (iface == NULL) 175 return (syncfd); 176 177 strlcpy(ifnam, iface, sizeof(ifnam)); 178 ttl = DHCP_SYNC_MCASTTTL; 179 if ((ttlstr = strchr(ifnam, ':')) != NULL) { 180 *ttlstr++ = '\0'; 181 ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr); 182 if (errstr) { 183 fprintf(stderr, "invalid multicast ttl %s: %s", 184 ttlstr, errstr); 185 goto fail; 186 } 187 } 188 189 memset(&ifr, 0, sizeof(ifr)); 190 strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 191 if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1) 192 goto fail; 193 194 memset(&sync_in, 0, sizeof(sync_in)); 195 addr = (struct sockaddr_in *)&ifr.ifr_addr; 196 sync_in.sin_family = AF_INET; 197 sync_in.sin_len = sizeof(sync_in); 198 sync_in.sin_addr.s_addr = addr->sin_addr.s_addr; 199 sync_in.sin_port = htons(port); 200 201 memset(&mreq, 0, sizeof(mreq)); 202 sync_out.sin_addr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR); 203 mreq.imr_multiaddr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR); 204 mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr; 205 206 if (setsockopt(syncfd, IPPROTO_IP, 207 IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { 208 log_warn("failed to add multicast membership to %s", 209 DHCP_SYNC_MCASTADDR); 210 goto fail; 211 } 212 if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 213 sizeof(ttl)) == -1) { 214 log_warn("failed to set multicast ttl to %u", ttl); 215 setsockopt(syncfd, IPPROTO_IP, 216 IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 217 goto fail; 218 } 219 220 if (sync_debug) 221 log_debug("using multicast dhcp sync %smode " 222 "(ttl %u, group %s, port %d)\n", 223 sendmcast ? "" : "receive ", 224 ttl, inet_ntoa(sync_out.sin_addr), port); 225 226 return (syncfd); 227 228 fail: 229 close(syncfd); 230 return (-1); 231 } 232 233 void 234 sync_recv(void) 235 { 236 struct dhcp_synchdr *hdr; 237 struct sockaddr_in addr; 238 struct dhcp_synctlv_hdr *tlv; 239 struct dhcp_synctlv_lease *lv; 240 struct lease *lease; 241 u_int8_t buf[DHCP_SYNC_MAXSIZE]; 242 u_int8_t hmac[2][DHCP_SYNC_HMAC_LEN]; 243 struct lease l, *lp; 244 u_int8_t *p; 245 socklen_t addr_len; 246 ssize_t len; 247 u_int hmac_len; 248 249 memset(&addr, 0, sizeof(addr)); 250 memset(buf, 0, sizeof(buf)); 251 252 addr_len = sizeof(addr); 253 if ((len = recvfrom(syncfd, buf, sizeof(buf), 0, 254 (struct sockaddr *)&addr, &addr_len)) < 1) 255 return; 256 if (addr.sin_addr.s_addr != htonl(INADDR_ANY) && 257 bcmp(&sync_in.sin_addr, &addr.sin_addr, 258 sizeof(addr.sin_addr)) == 0) 259 return; 260 261 /* Ignore invalid or truncated packets */ 262 hdr = (struct dhcp_synchdr *)buf; 263 if (len < sizeof(struct dhcp_synchdr) || 264 hdr->sh_version != DHCP_SYNC_VERSION || 265 hdr->sh_af != AF_INET || 266 len < ntohs(hdr->sh_length)) 267 goto trunc; 268 len = ntohs(hdr->sh_length); 269 270 /* Compute and validate HMAC */ 271 memcpy(hmac[0], hdr->sh_hmac, DHCP_SYNC_HMAC_LEN); 272 explicit_bzero(hdr->sh_hmac, DHCP_SYNC_HMAC_LEN); 273 HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len, 274 hmac[1], &hmac_len); 275 if (bcmp(hmac[0], hmac[1], DHCP_SYNC_HMAC_LEN) != 0) 276 goto trunc; 277 278 if (sync_debug) 279 log_info("%s(sync): received packet of %d bytes\n", 280 inet_ntoa(addr.sin_addr), (int)len); 281 282 p = (u_int8_t *)(hdr + 1); 283 while (len) { 284 tlv = (struct dhcp_synctlv_hdr *)p; 285 286 if (len < sizeof(struct dhcp_synctlv_hdr) || 287 len < ntohs(tlv->st_length)) 288 goto trunc; 289 290 switch (ntohs(tlv->st_type)) { 291 case DHCP_SYNC_LEASE: 292 lv = (struct dhcp_synctlv_lease *)tlv; 293 if (sizeof(*lv) > ntohs(tlv->st_length)) 294 goto trunc; 295 lease = find_lease_by_hw_addr( 296 lv->lv_hardware_addr.haddr, 297 lv->lv_hardware_addr.hlen); 298 if (lease == NULL) 299 lease = find_lease_by_ip_addr(lv->lv_ip_addr); 300 301 lp = &l; 302 memset(lp, 0, sizeof(*lp)); 303 lp->timestamp = ntohl(lv->lv_timestamp); 304 lp->starts = ntohl(lv->lv_starts); 305 lp->ends = ntohl(lv->lv_ends); 306 memcpy(&lp->ip_addr, &lv->lv_ip_addr, 307 sizeof(lp->ip_addr)); 308 memcpy(&lp->hardware_addr, &lv->lv_hardware_addr, 309 sizeof(lp->hardware_addr)); 310 log_info("DHCP_SYNC_LEASE from %s for hw %s -> ip %s, " 311 "start %lld, end %lld", 312 inet_ntoa(addr.sin_addr), 313 print_hw_addr(lp->hardware_addr.htype, 314 lp->hardware_addr.hlen, lp->hardware_addr.haddr), 315 piaddr(lp->ip_addr), 316 (long long)lp->starts, (long long)lp->ends); 317 /* now whack the lease in there */ 318 if (lease == NULL) { 319 enter_lease(lp); 320 write_leases(); 321 } 322 else if (lease->ends < lp->ends) 323 supersede_lease(lease, lp, 1); 324 else if (lease->ends > lp->ends) 325 /* 326 * our partner sent us a lease 327 * that is older than what we have, 328 * so re-educate them with what we 329 * know is newer. 330 */ 331 sync_lease(lease); 332 break; 333 case DHCP_SYNC_END: 334 goto done; 335 default: 336 printf("invalid type: %d\n", ntohs(tlv->st_type)); 337 goto trunc; 338 } 339 len -= ntohs(tlv->st_length); 340 p = ((u_int8_t *)tlv) + ntohs(tlv->st_length); 341 } 342 343 done: 344 return; 345 346 trunc: 347 if (sync_debug) 348 log_info("%s(sync): truncated or invalid packet\n", 349 inet_ntoa(addr.sin_addr)); 350 } 351 352 void 353 sync_send(struct iovec *iov, int iovlen) 354 { 355 struct sync_host *shost; 356 struct msghdr msg; 357 358 if (syncfd == -1) 359 return; 360 361 /* setup buffer */ 362 memset(&msg, 0, sizeof(msg)); 363 msg.msg_iov = iov; 364 msg.msg_iovlen = iovlen; 365 366 if (sendmcast) { 367 if (sync_debug) 368 log_info("sending multicast sync message\n"); 369 msg.msg_name = &sync_out; 370 msg.msg_namelen = sizeof(sync_out); 371 if (sendmsg(syncfd, &msg, 0) == -1) 372 log_warn("sending multicast sync message failed"); 373 } 374 375 LIST_FOREACH(shost, &sync_hosts, h_entry) { 376 if (sync_debug) 377 log_info("sending sync message to %s (%s)\n", 378 shost->h_name, inet_ntoa(shost->sh_addr.sin_addr)); 379 msg.msg_name = &shost->sh_addr; 380 msg.msg_namelen = sizeof(shost->sh_addr); 381 if (sendmsg(syncfd, &msg, 0) == -1) 382 log_warn("sending sync message failed"); 383 } 384 } 385 386 void 387 sync_lease(struct lease *lease) 388 { 389 struct iovec iov[4]; 390 struct dhcp_synchdr hdr; 391 struct dhcp_synctlv_lease lv; 392 struct dhcp_synctlv_hdr end; 393 char pad[DHCP_ALIGNBYTES]; 394 u_int16_t leaselen, padlen; 395 int i = 0; 396 HMAC_CTX ctx; 397 u_int hmac_len; 398 399 if (sync_key == NULL) 400 return; 401 402 memset(&hdr, 0, sizeof(hdr)); 403 memset(&lv, 0, sizeof(lv)); 404 memset(&pad, 0, sizeof(pad)); 405 406 HMAC_CTX_init(&ctx); 407 HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1()); 408 409 leaselen = sizeof(lv); 410 padlen = DHCP_ALIGN(leaselen) - leaselen; 411 412 /* Add DHCP sync packet header */ 413 hdr.sh_version = DHCP_SYNC_VERSION; 414 hdr.sh_af = AF_INET; 415 hdr.sh_counter = sync_counter++; 416 hdr.sh_length = htons(sizeof(hdr) + sizeof(lv) + padlen + sizeof(end)); 417 iov[i].iov_base = &hdr; 418 iov[i].iov_len = sizeof(hdr); 419 HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); 420 i++; 421 422 /* Add single DHCP sync address entry */ 423 lv.lv_type = htons(DHCP_SYNC_LEASE); 424 lv.lv_length = htons(leaselen + padlen); 425 lv.lv_timestamp = htonl(lease->timestamp); 426 lv.lv_starts = htonl(lease->starts); 427 lv.lv_ends = htonl(lease->ends); 428 memcpy(&lv.lv_ip_addr, &lease->ip_addr, sizeof(lv.lv_ip_addr)); 429 memcpy(&lv.lv_hardware_addr, &lease->hardware_addr, 430 sizeof(lv.lv_hardware_addr)); 431 log_info("sending DHCP_SYNC_LEASE for hw %s -> ip %s, start %d, " 432 "end %d", print_hw_addr(lv.lv_hardware_addr.htype, 433 lv.lv_hardware_addr.hlen, lv.lv_hardware_addr.haddr), 434 piaddr(lease->ip_addr), ntohl(lv.lv_starts), ntohl(lv.lv_ends)); 435 iov[i].iov_base = &lv; 436 iov[i].iov_len = sizeof(lv); 437 HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); 438 i++; 439 440 iov[i].iov_base = pad; 441 iov[i].iov_len = padlen; 442 HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); 443 i++; 444 445 /* Add end marker */ 446 end.st_type = htons(DHCP_SYNC_END); 447 end.st_length = htons(sizeof(end)); 448 iov[i].iov_base = &end; 449 iov[i].iov_len = sizeof(end); 450 HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); 451 i++; 452 453 HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len); 454 455 /* Send message to the target hosts */ 456 sync_send(iov, i); 457 HMAC_CTX_cleanup(&ctx); 458 } 459