GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/sync.c Lines: 0 242 0.0 %
Date: 2017-11-07 Branches: 0 111 0.0 %

Line Branch Exec Source
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
}