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