xref: /openbsd-src/usr.sbin/dhcpd/sync.c (revision 31e6f1a8e9a1170f8440ec70bcbdff9338229fd1)
1*31e6f1a8Ssthen /*	$OpenBSD: sync.c,v 1.25 2024/08/24 08:35:24 sthen Exp $	*/
25f515bebSbeck 
35f515bebSbeck /*
45f515bebSbeck  * Copyright (c) 2008 Bob Beck <beck@openbsd.org>
55f515bebSbeck  * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
65f515bebSbeck  *
75f515bebSbeck  * Permission to use, copy, modify, and distribute this software for any
85f515bebSbeck  * purpose with or without fee is hereby granted, provided that the above
95f515bebSbeck  * copyright notice and this permission notice appear in all copies.
105f515bebSbeck  *
115f515bebSbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
125f515bebSbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
135f515bebSbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
145f515bebSbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
155f515bebSbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
165f515bebSbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
175f515bebSbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
185f515bebSbeck  */
195f515bebSbeck 
20837cddffSkrw #include <sys/types.h>
215f515bebSbeck #include <sys/ioctl.h>
225f515bebSbeck #include <sys/queue.h>
23837cddffSkrw #include <sys/socket.h>
245f515bebSbeck 
255f515bebSbeck #include <net/if.h>
26837cddffSkrw 
275f515bebSbeck #include <arpa/inet.h>
285f515bebSbeck 
29837cddffSkrw #include <netinet/in.h>
305f515bebSbeck 
315f515bebSbeck #include <openssl/hmac.h>
325f515bebSbeck 
33837cddffSkrw #include <errno.h>
34837cddffSkrw #include <netdb.h>
35837cddffSkrw #include <sha1.h>
36837cddffSkrw #include <string.h>
37837cddffSkrw #include <syslog.h>
38837cddffSkrw #include <unistd.h>
39837cddffSkrw 
40837cddffSkrw #include "dhcp.h"
41837cddffSkrw #include "tree.h"
425f515bebSbeck #include "dhcpd.h"
43c525a185Skrw #include "log.h"
445f515bebSbeck #include "sync.h"
455f515bebSbeck 
465f515bebSbeck int sync_debug;
475f515bebSbeck 
485f515bebSbeck u_int32_t sync_counter;
49a84ef30bSbeck int syncfd = -1;
505f515bebSbeck int sendmcast;
515f515bebSbeck 
525f515bebSbeck struct sockaddr_in sync_in;
535f515bebSbeck struct sockaddr_in sync_out;
545f515bebSbeck static char *sync_key;
555f515bebSbeck 
565f515bebSbeck struct sync_host {
575f515bebSbeck 	LIST_ENTRY(sync_host)	h_entry;
585f515bebSbeck 
595f515bebSbeck 	char			*h_name;
605f515bebSbeck 	struct sockaddr_in	sh_addr;
615f515bebSbeck };
625f515bebSbeck LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts);
635f515bebSbeck 
645f515bebSbeck void	 sync_send(struct iovec *, int);
655f515bebSbeck 
665f515bebSbeck int
675f515bebSbeck sync_addhost(const char *name, u_short port)
685f515bebSbeck {
695f515bebSbeck 	struct addrinfo hints, *res, *res0;
705f515bebSbeck 	struct sync_host *shost;
715f515bebSbeck 	struct sockaddr_in *addr = NULL;
725f515bebSbeck 
7387fe6823Smestre 	memset(&hints, 0, sizeof(hints));
745f515bebSbeck 	hints.ai_family = PF_UNSPEC;
755f515bebSbeck 	hints.ai_socktype = SOCK_STREAM;
765f515bebSbeck 	if (getaddrinfo(name, NULL, &hints, &res0) != 0)
775f515bebSbeck 		return (EINVAL);
785f515bebSbeck 	for (res = res0; res != NULL; res = res->ai_next) {
795f515bebSbeck 		if (addr == NULL && res->ai_family == AF_INET) {
805f515bebSbeck 			addr = (struct sockaddr_in *)res->ai_addr;
815f515bebSbeck 			break;
825f515bebSbeck 		}
835f515bebSbeck 	}
845f515bebSbeck 	if (addr == NULL) {
855f515bebSbeck 		freeaddrinfo(res0);
865f515bebSbeck 		return (EINVAL);
875f515bebSbeck 	}
885f515bebSbeck 	if ((shost = (struct sync_host *)
895f515bebSbeck 	    calloc(1, sizeof(struct sync_host))) == NULL) {
905f515bebSbeck 		freeaddrinfo(res0);
915f515bebSbeck 		return (ENOMEM);
925f515bebSbeck 	}
937a904ddaSkrw 	shost->h_name = strdup(name);
947a904ddaSkrw 	if (shost->h_name == NULL) {
955f515bebSbeck 		free(shost);
965f515bebSbeck 		freeaddrinfo(res0);
975f515bebSbeck 		return (ENOMEM);
985f515bebSbeck 	}
995f515bebSbeck 
1005f515bebSbeck 	shost->sh_addr.sin_family = AF_INET;
1015f515bebSbeck 	shost->sh_addr.sin_port = htons(port);
1025f515bebSbeck 	shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
1035f515bebSbeck 	freeaddrinfo(res0);
1045f515bebSbeck 
1055f515bebSbeck 	LIST_INSERT_HEAD(&sync_hosts, shost, h_entry);
1065f515bebSbeck 
1075f515bebSbeck 	if (sync_debug)
108c525a185Skrw 		log_info("added dhcp sync host %s (address %s, port %d)\n",
1093af700feSclaudio 		    shost->h_name, inet_ntoa(shost->sh_addr.sin_addr), port);
1105f515bebSbeck 
1115f515bebSbeck 	return (0);
1125f515bebSbeck }
1135f515bebSbeck 
1145f515bebSbeck int
1155f515bebSbeck sync_init(const char *iface, const char *baddr, u_short port)
1165f515bebSbeck {
1175f515bebSbeck 	int one = 1;
1185f515bebSbeck 	u_int8_t ttl;
1195f515bebSbeck 	struct ifreq ifr;
1205f515bebSbeck 	struct ip_mreq mreq;
1215f515bebSbeck 	struct sockaddr_in *addr;
1225f515bebSbeck 	char ifnam[IFNAMSIZ], *ttlstr;
1235f515bebSbeck 	const char *errstr;
1245f515bebSbeck 	struct in_addr ina;
1255f515bebSbeck 
1265f515bebSbeck 	if (iface != NULL)
1275f515bebSbeck 		sendmcast++;
1285f515bebSbeck 
12987fe6823Smestre 	memset(&ina, 0, sizeof(ina));
1305f515bebSbeck 	if (baddr != NULL) {
1315f515bebSbeck 		if (inet_pton(AF_INET, baddr, &ina) != 1) {
1325f515bebSbeck 			ina.s_addr = htonl(INADDR_ANY);
1335f515bebSbeck 			if (iface == NULL)
1345f515bebSbeck 				iface = baddr;
1355f515bebSbeck 			else if (iface != NULL && strcmp(baddr, iface) != 0) {
1365f515bebSbeck 				fprintf(stderr, "multicast interface does "
1375f515bebSbeck 				    "not match");
1385f515bebSbeck 				return (-1);
1395f515bebSbeck 			}
1405f515bebSbeck 		}
1415f515bebSbeck 	}
1425f515bebSbeck 
1435f515bebSbeck 	sync_key = SHA1File(DHCP_SYNC_KEY, NULL);
1445f515bebSbeck 	if (sync_key == NULL) {
1455f515bebSbeck 		if (errno != ENOENT) {
146a76b277aSkrw 			log_warn("failed to open sync key");
1475f515bebSbeck 			return (-1);
1485f515bebSbeck 		}
1495f515bebSbeck 		/* Use empty key by default */
1505f515bebSbeck 		sync_key = "";
1515f515bebSbeck 	}
1525f515bebSbeck 
1535f515bebSbeck 	syncfd = socket(AF_INET, SOCK_DGRAM, 0);
1545f515bebSbeck 	if (syncfd == -1)
1555f515bebSbeck 		return (-1);
1565f515bebSbeck 
1575f515bebSbeck 	if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one,
1585f515bebSbeck 	    sizeof(one)) == -1)
1595f515bebSbeck 		goto fail;
1605f515bebSbeck 
16187fe6823Smestre 	memset(&sync_out, 0, sizeof(sync_out));
1625f515bebSbeck 	sync_out.sin_family = AF_INET;
1635f515bebSbeck 	sync_out.sin_len = sizeof(sync_out);
1645f515bebSbeck 	sync_out.sin_addr.s_addr = ina.s_addr;
1655f515bebSbeck 	if (baddr == NULL && iface == NULL)
1665f515bebSbeck 		sync_out.sin_port = 0;
1675f515bebSbeck 	else
1685f515bebSbeck 		sync_out.sin_port = htons(port);
1695f515bebSbeck 
1705f515bebSbeck 	if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1)
1715f515bebSbeck 		goto fail;
1725f515bebSbeck 
1735f515bebSbeck 	/* Don't use multicast messages */
1745f515bebSbeck 	if (iface == NULL)
1755f515bebSbeck 		return (syncfd);
1765f515bebSbeck 
1775f515bebSbeck 	strlcpy(ifnam, iface, sizeof(ifnam));
1785f515bebSbeck 	ttl = DHCP_SYNC_MCASTTTL;
1795f515bebSbeck 	if ((ttlstr = strchr(ifnam, ':')) != NULL) {
1805f515bebSbeck 		*ttlstr++ = '\0';
1815f515bebSbeck 		ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr);
1825f515bebSbeck 		if (errstr) {
1835f515bebSbeck 			fprintf(stderr, "invalid multicast ttl %s: %s",
1845f515bebSbeck 			    ttlstr, errstr);
1855f515bebSbeck 			goto fail;
1865f515bebSbeck 		}
1875f515bebSbeck 	}
1885f515bebSbeck 
18987fe6823Smestre 	memset(&ifr, 0, sizeof(ifr));
1905f515bebSbeck 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
1915f515bebSbeck 	if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1)
1925f515bebSbeck 		goto fail;
1935f515bebSbeck 
19487fe6823Smestre 	memset(&sync_in, 0, sizeof(sync_in));
1955f515bebSbeck 	addr = (struct sockaddr_in *)&ifr.ifr_addr;
1965f515bebSbeck 	sync_in.sin_family = AF_INET;
1975f515bebSbeck 	sync_in.sin_len = sizeof(sync_in);
1985f515bebSbeck 	sync_in.sin_addr.s_addr = addr->sin_addr.s_addr;
1995f515bebSbeck 	sync_in.sin_port = htons(port);
2005f515bebSbeck 
20187fe6823Smestre 	memset(&mreq, 0, sizeof(mreq));
2025f515bebSbeck 	sync_out.sin_addr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR);
2035f515bebSbeck 	mreq.imr_multiaddr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR);
2045f515bebSbeck 	mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr;
2055f515bebSbeck 
2065f515bebSbeck 	if (setsockopt(syncfd, IPPROTO_IP,
2075f515bebSbeck 	    IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
208a76b277aSkrw 		log_warn("failed to add multicast membership to %s",
209a76b277aSkrw 		    DHCP_SYNC_MCASTADDR);
2105f515bebSbeck 		goto fail;
2115f515bebSbeck 	}
2125f515bebSbeck 	if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
2139bb003e4Sclaudio 	    sizeof(ttl)) == -1) {
214a76b277aSkrw 		log_warn("failed to set multicast ttl to %u", ttl);
2155f515bebSbeck 		setsockopt(syncfd, IPPROTO_IP,
2165f515bebSbeck 		    IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
2175f515bebSbeck 		goto fail;
2185f515bebSbeck 	}
2195f515bebSbeck 
2205f515bebSbeck 	if (sync_debug)
221c525a185Skrw 		log_debug("using multicast dhcp sync %smode "
2225f515bebSbeck 		    "(ttl %u, group %s, port %d)\n",
2235f515bebSbeck 		    sendmcast ? "" : "receive ",
2245f515bebSbeck 		    ttl, inet_ntoa(sync_out.sin_addr), port);
2255f515bebSbeck 
2265f515bebSbeck 	return (syncfd);
2275f515bebSbeck 
2285f515bebSbeck  fail:
2295f515bebSbeck 	close(syncfd);
2305f515bebSbeck 	return (-1);
2315f515bebSbeck }
2325f515bebSbeck 
2335f515bebSbeck void
2345f515bebSbeck sync_recv(void)
2355f515bebSbeck {
2365f515bebSbeck 	struct dhcp_synchdr *hdr;
2375f515bebSbeck 	struct sockaddr_in addr;
2385f515bebSbeck 	struct dhcp_synctlv_hdr *tlv;
2395f515bebSbeck 	struct dhcp_synctlv_lease *lv;
2405f515bebSbeck 	struct lease	*lease;
2415f515bebSbeck 	u_int8_t buf[DHCP_SYNC_MAXSIZE];
2425f515bebSbeck 	u_int8_t hmac[2][DHCP_SYNC_HMAC_LEN];
2435f515bebSbeck 	struct lease l, *lp;
2445f515bebSbeck 	u_int8_t *p;
2455f515bebSbeck 	socklen_t addr_len;
2465f515bebSbeck 	ssize_t len;
2475f515bebSbeck 	u_int hmac_len;
2485f515bebSbeck 
24987fe6823Smestre 	memset(&addr, 0, sizeof(addr));
25087fe6823Smestre 	memset(buf, 0, sizeof(buf));
2515f515bebSbeck 
2525f515bebSbeck 	addr_len = sizeof(addr);
2535f515bebSbeck 	if ((len = recvfrom(syncfd, buf, sizeof(buf), 0,
2545f515bebSbeck 	    (struct sockaddr *)&addr, &addr_len)) < 1)
2555f515bebSbeck 		return;
2565f515bebSbeck 	if (addr.sin_addr.s_addr != htonl(INADDR_ANY) &&
2575f515bebSbeck 	    bcmp(&sync_in.sin_addr, &addr.sin_addr,
2585f515bebSbeck 	    sizeof(addr.sin_addr)) == 0)
2595f515bebSbeck 		return;
2605f515bebSbeck 
2615f515bebSbeck 	/* Ignore invalid or truncated packets */
2625f515bebSbeck 	hdr = (struct dhcp_synchdr *)buf;
2635f515bebSbeck 	if (len < sizeof(struct dhcp_synchdr) ||
2645f515bebSbeck 	    hdr->sh_version != DHCP_SYNC_VERSION ||
2655f515bebSbeck 	    hdr->sh_af != AF_INET ||
2665f515bebSbeck 	    len < ntohs(hdr->sh_length))
2675f515bebSbeck 		goto trunc;
2685f515bebSbeck 	len = ntohs(hdr->sh_length);
2695f515bebSbeck 
2705f515bebSbeck 	/* Compute and validate HMAC */
2711674d7d2Skrw 	memcpy(hmac[0], hdr->sh_hmac, DHCP_SYNC_HMAC_LEN);
27287fe6823Smestre 	explicit_bzero(hdr->sh_hmac, DHCP_SYNC_HMAC_LEN);
2735f515bebSbeck 	HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len,
2745f515bebSbeck 	    hmac[1], &hmac_len);
2755f515bebSbeck 	if (bcmp(hmac[0], hmac[1], DHCP_SYNC_HMAC_LEN) != 0)
2765f515bebSbeck 		goto trunc;
2775f515bebSbeck 
2785f515bebSbeck 	if (sync_debug)
279c525a185Skrw 		log_info("%s(sync): received packet of %d bytes\n",
2805f515bebSbeck 		    inet_ntoa(addr.sin_addr), (int)len);
2815f515bebSbeck 
2825f515bebSbeck 	p = (u_int8_t *)(hdr + 1);
2835f515bebSbeck 	while (len) {
2845f515bebSbeck 		tlv = (struct dhcp_synctlv_hdr *)p;
2855f515bebSbeck 
2865f515bebSbeck 		if (len < sizeof(struct dhcp_synctlv_hdr) ||
2875f515bebSbeck 		    len < ntohs(tlv->st_length))
2885f515bebSbeck 			goto trunc;
2895f515bebSbeck 
2905f515bebSbeck 		switch (ntohs(tlv->st_type)) {
2915f515bebSbeck 		case DHCP_SYNC_LEASE:
2925f515bebSbeck 			lv = (struct dhcp_synctlv_lease *)tlv;
2935f515bebSbeck 			if (sizeof(*lv) > ntohs(tlv->st_length))
2945f515bebSbeck 				goto trunc;
295dc53f43cSkrw 			lease = find_lease_by_hw_addr(
296aa92cf00Skrw 			    lv->lv_hardware_addr.haddr,
297dc53f43cSkrw 			    lv->lv_hardware_addr.hlen);
298dc53f43cSkrw 			if (lease == NULL)
299dc53f43cSkrw 				lease = find_lease_by_ip_addr(lv->lv_ip_addr);
3005f515bebSbeck 
3015f515bebSbeck 			lp = &l;
3025f515bebSbeck 			memset(lp, 0, sizeof(*lp));
303aa92cf00Skrw 			lp->timestamp = ntohl(lv->lv_timestamp);
304aa92cf00Skrw 			lp->starts = ntohl(lv->lv_starts);
305aa92cf00Skrw 			lp->ends = ntohl(lv->lv_ends);
306aa92cf00Skrw 			memcpy(&lp->ip_addr, &lv->lv_ip_addr,
3075f515bebSbeck 			    sizeof(lp->ip_addr));
308aa92cf00Skrw 			memcpy(&lp->hardware_addr, &lv->lv_hardware_addr,
3095f515bebSbeck 			    sizeof(lp->hardware_addr));
310*31e6f1a8Ssthen 			log_debug("DHCP_SYNC_LEASE from %s for hw %s -> ip %s, "
31196bc3eceSkrw 			    "start %lld, end %lld",
3125f515bebSbeck 			    inet_ntoa(addr.sin_addr),
3135f515bebSbeck 			    print_hw_addr(lp->hardware_addr.htype,
3143af700feSclaudio 			    lp->hardware_addr.hlen, lp->hardware_addr.haddr),
31596bc3eceSkrw 			    piaddr(lp->ip_addr),
31696bc3eceSkrw 			    (long long)lp->starts, (long long)lp->ends);
3175f515bebSbeck 			/* now whack the lease in there */
3185f515bebSbeck 			if (lease == NULL) {
3195f515bebSbeck 				enter_lease(lp);
3205f515bebSbeck 				write_leases();
3215f515bebSbeck 			}
3225f515bebSbeck 			else if (lease->ends < lp->ends)
3235f515bebSbeck 				supersede_lease(lease, lp, 1);
3245f515bebSbeck 			else if (lease->ends > lp->ends)
3255f515bebSbeck 				/*
3265f515bebSbeck 				 * our partner sent us a lease
3275f515bebSbeck 				 * that is older than what we have,
3285f515bebSbeck 				 * so re-educate them with what we
3295f515bebSbeck 				 * know is newer.
3305f515bebSbeck 				 */
3315f515bebSbeck 				sync_lease(lease);
3325f515bebSbeck 			break;
3335f515bebSbeck 		case DHCP_SYNC_END:
3345f515bebSbeck 			goto done;
3355f515bebSbeck 		default:
3365f515bebSbeck 			printf("invalid type: %d\n", ntohs(tlv->st_type));
3375f515bebSbeck 			goto trunc;
3385f515bebSbeck 		}
3395f515bebSbeck 		len -= ntohs(tlv->st_length);
3405f515bebSbeck 		p = ((u_int8_t *)tlv) + ntohs(tlv->st_length);
3415f515bebSbeck 	}
3425f515bebSbeck 
3435f515bebSbeck  done:
3445f515bebSbeck 	return;
3455f515bebSbeck 
3465f515bebSbeck  trunc:
3475f515bebSbeck 	if (sync_debug)
348c525a185Skrw 		log_info("%s(sync): truncated or invalid packet\n",
3495f515bebSbeck 		    inet_ntoa(addr.sin_addr));
3505f515bebSbeck }
3515f515bebSbeck 
3525f515bebSbeck void
3535f515bebSbeck sync_send(struct iovec *iov, int iovlen)
3545f515bebSbeck {
3555f515bebSbeck 	struct sync_host *shost;
3565f515bebSbeck 	struct msghdr msg;
3575f515bebSbeck 
3587eab935eSbeck 	if (syncfd == -1)
3597eab935eSbeck 		return;
3607eab935eSbeck 
3615f515bebSbeck 	/* setup buffer */
36287fe6823Smestre 	memset(&msg, 0, sizeof(msg));
3635f515bebSbeck 	msg.msg_iov = iov;
3645f515bebSbeck 	msg.msg_iovlen = iovlen;
3655f515bebSbeck 
3665f515bebSbeck 	if (sendmcast) {
3675f515bebSbeck 		if (sync_debug)
368c525a185Skrw 			log_info("sending multicast sync message\n");
3695f515bebSbeck 		msg.msg_name = &sync_out;
3705f515bebSbeck 		msg.msg_namelen = sizeof(sync_out);
3713af700feSclaudio 		if (sendmsg(syncfd, &msg, 0) == -1)
3720438cf0aSkrw 			log_warn("sending multicast sync message failed");
3735f515bebSbeck 	}
3745f515bebSbeck 
3755f515bebSbeck 	LIST_FOREACH(shost, &sync_hosts, h_entry) {
3765f515bebSbeck 		if (sync_debug)
377c525a185Skrw 			log_info("sending sync message to %s (%s)\n",
3785f515bebSbeck 			    shost->h_name, inet_ntoa(shost->sh_addr.sin_addr));
3795f515bebSbeck 		msg.msg_name = &shost->sh_addr;
3805f515bebSbeck 		msg.msg_namelen = sizeof(shost->sh_addr);
3813af700feSclaudio 		if (sendmsg(syncfd, &msg, 0) == -1)
3820438cf0aSkrw 			log_warn("sending sync message failed");
3835f515bebSbeck 	}
3845f515bebSbeck }
3855f515bebSbeck 
3865f515bebSbeck void
3875f515bebSbeck sync_lease(struct lease *lease)
3885f515bebSbeck {
389ed4ccfd6Sderaadt 	struct iovec iov[4];
3905f515bebSbeck 	struct dhcp_synchdr hdr;
391aa92cf00Skrw 	struct dhcp_synctlv_lease lv;
3925f515bebSbeck 	struct dhcp_synctlv_hdr end;
3933aa44215Sderaadt 	char pad[DHCP_ALIGNBYTES];
3943aa44215Sderaadt 	u_int16_t leaselen, padlen;
3955f515bebSbeck 	int i = 0;
3968f4b0a11Stb 	HMAC_CTX *ctx;
3975f515bebSbeck 	u_int hmac_len;
3985f515bebSbeck 
399a07ad480Sbeck 	if (sync_key == NULL)
400a07ad480Sbeck 		return;
401a07ad480Sbeck 
40287fe6823Smestre 	memset(&hdr, 0, sizeof(hdr));
40387fe6823Smestre 	memset(&lv, 0, sizeof(lv));
40487fe6823Smestre 	memset(&pad, 0, sizeof(pad));
4055f515bebSbeck 
4068f4b0a11Stb 	if ((ctx = HMAC_CTX_new()) == NULL)
4078f4b0a11Stb 		goto bad;
4088f4b0a11Stb 	if (!HMAC_Init_ex(ctx, sync_key, strlen(sync_key), EVP_sha1(), NULL))
4098f4b0a11Stb 		goto bad;
4105f515bebSbeck 
411aa92cf00Skrw 	leaselen = sizeof(lv);
4123aa44215Sderaadt 	padlen = DHCP_ALIGN(leaselen) - leaselen;
4133aa44215Sderaadt 
4145f515bebSbeck 	/* Add DHCP sync packet header */
4155f515bebSbeck 	hdr.sh_version = DHCP_SYNC_VERSION;
4165f515bebSbeck 	hdr.sh_af = AF_INET;
4175f515bebSbeck 	hdr.sh_counter = sync_counter++;
418aa92cf00Skrw 	hdr.sh_length = htons(sizeof(hdr) + sizeof(lv) + padlen + sizeof(end));
4195f515bebSbeck 	iov[i].iov_base = &hdr;
4205f515bebSbeck 	iov[i].iov_len = sizeof(hdr);
4218f4b0a11Stb 	if (!HMAC_Update(ctx, iov[i].iov_base, iov[i].iov_len))
4228f4b0a11Stb 		goto bad;
4235f515bebSbeck 	i++;
4245f515bebSbeck 
4255f515bebSbeck 	/* Add single DHCP sync address entry */
426aa92cf00Skrw 	lv.lv_type = htons(DHCP_SYNC_LEASE);
427aa92cf00Skrw 	lv.lv_length = htons(leaselen + padlen);
428aa92cf00Skrw 	lv.lv_timestamp = htonl(lease->timestamp);
429aa92cf00Skrw 	lv.lv_starts = htonl(lease->starts);
430aa92cf00Skrw 	lv.lv_ends =  htonl(lease->ends);
431aa92cf00Skrw 	memcpy(&lv.lv_ip_addr, &lease->ip_addr, sizeof(lv.lv_ip_addr));
432aa92cf00Skrw 	memcpy(&lv.lv_hardware_addr, &lease->hardware_addr,
433aa92cf00Skrw 	    sizeof(lv.lv_hardware_addr));
434*31e6f1a8Ssthen 	log_debug("sending DHCP_SYNC_LEASE for hw %s -> ip %s, start %d, "
43535318e8fSkrw 	    "end %d", print_hw_addr(lv.lv_hardware_addr.htype,
43635318e8fSkrw 	    lv.lv_hardware_addr.hlen, lv.lv_hardware_addr.haddr),
43735318e8fSkrw 	    piaddr(lease->ip_addr), ntohl(lv.lv_starts), ntohl(lv.lv_ends));
438aa92cf00Skrw 	iov[i].iov_base = &lv;
439aa92cf00Skrw 	iov[i].iov_len = sizeof(lv);
4408f4b0a11Stb 	if (!HMAC_Update(ctx, iov[i].iov_base, iov[i].iov_len))
4418f4b0a11Stb 		goto bad;
4425f515bebSbeck 	i++;
4435f515bebSbeck 
4443aa44215Sderaadt 	iov[i].iov_base = pad;
4453aa44215Sderaadt 	iov[i].iov_len = padlen;
4468f4b0a11Stb 	if (!HMAC_Update(ctx, iov[i].iov_base, iov[i].iov_len))
4478f4b0a11Stb 		goto bad;
4483aa44215Sderaadt 	i++;
4493aa44215Sderaadt 
4505f515bebSbeck 	/* Add end marker */
4515f515bebSbeck 	end.st_type = htons(DHCP_SYNC_END);
4525f515bebSbeck 	end.st_length = htons(sizeof(end));
4535f515bebSbeck 	iov[i].iov_base = &end;
4545f515bebSbeck 	iov[i].iov_len = sizeof(end);
4558f4b0a11Stb 	if (!HMAC_Update(ctx, iov[i].iov_base, iov[i].iov_len))
4568f4b0a11Stb 		goto bad;
4575f515bebSbeck 	i++;
4585f515bebSbeck 
4598f4b0a11Stb 	if (!HMAC_Final(ctx, hdr.sh_hmac, &hmac_len))
4608f4b0a11Stb 		goto bad;
4615f515bebSbeck 
4625f515bebSbeck 	/* Send message to the target hosts */
4635f515bebSbeck 	sync_send(iov, i);
4648f4b0a11Stb 
4658f4b0a11Stb  bad:
4668f4b0a11Stb 	HMAC_CTX_free(ctx);
4675f515bebSbeck }
468