Home | History | Annotate | Download | only in dhcpcd-6.8.2
      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 #include <sys/file.h>
     29 #include <errno.h>
     30 #include <fcntl.h>
     31 #include <inttypes.h>
     32 #include <stddef.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <unistd.h>
     37 
     38 #include "config.h"
     39 #include "auth.h"
     40 #include "crypt/crypt.h"
     41 #include "dhcp.h"
     42 #include "dhcp6.h"
     43 #include "dhcpcd.h"
     44 
     45 #ifdef __sun
     46 #define htonll
     47 #define ntohll
     48 #endif
     49 
     50 #ifndef htonll
     51 #if (BYTE_ORDER == LITTLE_ENDIAN)
     52 static inline uint64_t
     53 htonll(uint64_t x)
     54 {
     55 
     56 	return (uint64_t)htonl((uint32_t)(x >> 32)) |
     57 	    (uint64_t)htonl((uint32_t)(x & 0xffffffff)) << 32;
     58 }
     59 #else	/* (BYTE_ORDER == LITTLE_ENDIAN) */
     60 #define htonll(x) (x)
     61 #endif
     62 #endif  /* htonll */
     63 
     64 #ifndef ntohll
     65 #if (BYTE_ORDER == LITTLE_ENDIAN)
     66 static inline uint64_t
     67 ntohll(uint64_t x)
     68 {
     69 
     70 	return (uint64_t)ntohl((uint32_t)(x >> 32)) |
     71 	    (uint64_t)ntohl((uint32_t)(x & 0xffffffff)) << 32;
     72 }
     73 #else	/* (BYTE_ORDER == LITTLE_ENDIAN) */
     74 #define ntohll(x) (x)
     75 #endif
     76 #endif  /* ntohll */
     77 
     78 #define HMAC_LENGTH	16
     79 
     80 void
     81 dhcp_auth_reset(struct authstate *state)
     82 {
     83 
     84 	state->replay = 0;
     85 	if (state->token) {
     86 		free(state->token->key);
     87 		free(state->token->realm);
     88 		free(state->token);
     89 		state->token = NULL;
     90 	}
     91 	if (state->reconf) {
     92 		free(state->reconf->key);
     93 		free(state->reconf->realm);
     94 		free(state->reconf);
     95 		state->reconf = NULL;
     96 	}
     97 }
     98 
     99 /*
    100  * Authenticate a DHCP message.
    101  * m and mlen refer to the whole message.
    102  * t is the DHCP type, pass it 4 or 6.
    103  * data and dlen refer to the authentication option within the message.
    104  */
    105 const struct token *
    106 dhcp_auth_validate(struct authstate *state, const struct auth *auth,
    107     const uint8_t *m, size_t mlen, int mp,  int mt,
    108     const uint8_t *data, size_t dlen)
    109 {
    110 	uint8_t protocol, algorithm, rdm, *mm, type;
    111 	uint64_t replay;
    112 	uint32_t secretid;
    113 	const uint8_t *d, *realm;
    114 	size_t realm_len;
    115 	const struct token *t;
    116 	time_t now;
    117 	uint8_t hmac[HMAC_LENGTH];
    118 
    119 	if (dlen < 3 + sizeof(replay)) {
    120 		errno = EINVAL;
    121 		return NULL;
    122 	}
    123 
    124 	/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
    125 	if (data < m || data > m + mlen || data + dlen > m + mlen) {
    126 		errno = ERANGE;
    127 		return NULL;
    128 	}
    129 
    130 	d = data;
    131 	protocol = *d++;
    132 	algorithm = *d++;
    133 	rdm = *d++;
    134 	if (!(auth->options & DHCPCD_AUTH_SEND)) {
    135 		/* If we didn't send any authorisation, it can only be a
    136 		 * reconfigure key */
    137 		if (protocol != AUTH_PROTO_RECONFKEY) {
    138 			errno = EINVAL;
    139 			return NULL;
    140 		}
    141 	} else if (protocol != auth->protocol ||
    142 		    algorithm != auth->algorithm ||
    143 		    rdm != auth->rdm)
    144 	{
    145 		/* As we don't require authentication, we should still
    146 		 * accept a reconfigure key */
    147 		if (protocol != AUTH_PROTO_RECONFKEY ||
    148 		    auth->options & DHCPCD_AUTH_REQUIRE)
    149 		{
    150 			errno = EPERM;
    151 			return NULL;
    152 		}
    153 	}
    154 	dlen -= 3;
    155 
    156 	memcpy(&replay, d, sizeof(replay));
    157 	replay = ntohll(replay);
    158 	if (state->token) {
    159 		if (state->replay == (replay ^ 0x8000000000000000ULL)) {
    160 			/* We don't know if the singular point is increasing
    161 			 * or decreasing. */
    162 			errno = EPERM;
    163 			return NULL;
    164 		}
    165 		if ((uint64_t)(replay - state->replay) <= 0) {
    166 			/* Replay attack detected */
    167 			errno = EPERM;
    168 			return NULL;
    169 		}
    170 	}
    171 	d+= sizeof(replay);
    172 	dlen -= sizeof(replay);
    173 
    174 	realm = NULL;
    175 	realm_len = 0;
    176 
    177 	/* Extract realm and secret.
    178 	 * Rest of data is MAC. */
    179 	switch (protocol) {
    180 	case AUTH_PROTO_TOKEN:
    181 		secretid = 0;
    182 		break;
    183 	case AUTH_PROTO_DELAYED:
    184 		if (dlen < sizeof(secretid) + sizeof(hmac)) {
    185 			errno = EINVAL;
    186 			return NULL;
    187 		}
    188 		memcpy(&secretid, d, sizeof(secretid));
    189 		d += sizeof(secretid);
    190 		dlen -= sizeof(secretid);
    191 		break;
    192 	case AUTH_PROTO_DELAYEDREALM:
    193 		if (dlen < sizeof(secretid) + sizeof(hmac)) {
    194 			errno = EINVAL;
    195 			return NULL;
    196 		}
    197 		realm_len = dlen - (sizeof(secretid) + sizeof(hmac));
    198 		if (realm_len) {
    199 			realm = d;
    200 			d += realm_len;
    201 			dlen -= realm_len;
    202 		}
    203 		memcpy(&secretid, d, sizeof(secretid));
    204 		d += sizeof(secretid);
    205 		dlen -= sizeof(secretid);
    206 		break;
    207 	case AUTH_PROTO_RECONFKEY:
    208 		if (dlen != 1 + 16) {
    209 			errno = EINVAL;
    210 			return NULL;
    211 		}
    212 		type = *d++;
    213 		dlen--;
    214 		switch (type) {
    215 		case 1:
    216 			if ((mp == 4 && mt == DHCP_ACK) ||
    217 			    (mp == 6 && mt == DHCP6_REPLY))
    218 			{
    219 				if (state->reconf == NULL) {
    220 					state->reconf =
    221 					    malloc(sizeof(*state->reconf));
    222 					if (state->reconf == NULL)
    223 						return NULL;
    224 					state->reconf->key = malloc(16);
    225 					if (state->reconf->key == NULL) {
    226 						free(state->reconf);
    227 						state->reconf = NULL;
    228 						return NULL;
    229 					}
    230 					state->reconf->secretid = 0;
    231 					state->reconf->expire = 0;
    232 					state->reconf->realm = NULL;
    233 					state->reconf->realm_len = 0;
    234 					state->reconf->key_len = 16;
    235 				}
    236 				memcpy(state->reconf->key, d, 16);
    237 			} else {
    238 				errno = EINVAL;
    239 				return NULL;
    240 			}
    241 			if (state->reconf == NULL)
    242 				errno = ENOENT;
    243 			/* Free the old token so we log acceptance */
    244 			if (state->token) {
    245 				free(state->token);
    246 				state->token = NULL;
    247 			}
    248 			/* Nothing to validate, just accepting the key */
    249 			return state->reconf;
    250 		case 2:
    251 			if (!((mp == 4 && mt == DHCP_FORCERENEW) ||
    252 			    (mp == 6 && mt == DHCP6_RECONFIGURE)))
    253 			{
    254 				errno = EINVAL;
    255 				return NULL;
    256 			}
    257 			if (state->reconf == NULL) {
    258 				errno = ENOENT;
    259 				return NULL;
    260 			}
    261 			t = state->reconf;
    262 			goto gottoken;
    263 		default:
    264 			errno = EINVAL;
    265 			return NULL;
    266 		}
    267 	default:
    268 		errno = ENOTSUP;
    269 		return NULL;
    270 	}
    271 
    272 	/* Find a token for the realm and secret */
    273 	secretid = ntohl(secretid);
    274 	TAILQ_FOREACH(t, &auth->tokens, next) {
    275 		if (t->secretid == secretid &&
    276 		    t->realm_len == realm_len &&
    277 		    (t->realm_len == 0 ||
    278 		    memcmp(t->realm, realm, t->realm_len) == 0))
    279 			break;
    280 	}
    281 	if (t == NULL) {
    282 		errno = ESRCH;
    283 		return NULL;
    284 	}
    285 	if (t->expire) {
    286 		if (time(&now) == -1)
    287 			return NULL;
    288 		if (t->expire < now) {
    289 			errno = EFAULT;
    290 			return NULL;
    291 		}
    292 	}
    293 
    294 gottoken:
    295 	/* First message from the server */
    296 	if (state->token &&
    297 	    (state->token->secretid != t->secretid ||
    298 	    state->token->realm_len != t->realm_len ||
    299 	    memcmp(state->token->realm, t->realm, t->realm_len)))
    300 	{
    301 		errno = EPERM;
    302 		return NULL;
    303 	}
    304 
    305 	/* Special case as no hashing needs to be done. */
    306 	if (protocol == AUTH_PROTO_TOKEN) {
    307 		if (dlen != t->key_len || memcmp(d, t->key, dlen)) {
    308 			errno = EPERM;
    309 			return NULL;
    310 		}
    311 		goto finish;
    312 	}
    313 
    314 	/* Make a duplicate of the message, but zero out the MAC part */
    315 	mm = malloc(mlen);
    316 	if (mm == NULL)
    317 		return NULL;
    318 	memcpy(mm, m, mlen);
    319 	memset(mm + (d - m), 0, dlen);
    320 
    321 	/* RFC3318, section 5.2 - zero giaddr and hops */
    322 	if (mp == 4) {
    323 		*(mm + offsetof(struct dhcp_message, hwopcount)) = '\0';
    324 		memset(mm + offsetof(struct dhcp_message, giaddr), 0, 4);
    325 	}
    326 
    327 	memset(hmac, 0, sizeof(hmac));
    328 	switch (algorithm) {
    329 	case AUTH_ALG_HMAC_MD5:
    330 		hmac_md5(mm, mlen, t->key, t->key_len, hmac);
    331 		break;
    332 	default:
    333 		errno = ENOSYS;
    334 		free(mm);
    335 		return NULL;
    336 	}
    337 
    338 	free(mm);
    339 	if (memcmp(d, &hmac, dlen)) {
    340 		errno = EPERM;
    341 		return NULL;
    342 	}
    343 
    344 finish:
    345 	/* If we got here then authentication passed */
    346 	state->replay = replay;
    347 	if (state->token == NULL) {
    348 		/* We cannot just save a pointer because a reconfigure will
    349 		 * recreate the token list. So we duplicate it. */
    350 		state->token = malloc(sizeof(*state->token));
    351 		if (state->token) {
    352 			state->token->secretid = t->secretid;
    353 			state->token->key = malloc(t->key_len);
    354 			if (state->token->key) {
    355 				state->token->key_len = t->key_len;
    356 				memcpy(state->token->key, t->key, t->key_len);
    357 			} else {
    358 				free(state->token);
    359 				state->token = NULL;
    360 				return NULL;
    361 			}
    362 			if (t->realm_len) {
    363 				state->token->realm = malloc(t->realm_len);
    364 				if (state->token->realm) {
    365 					state->token->realm_len = t->realm_len;
    366 					memcpy(state->token->realm, t->realm,
    367 					    t->realm_len);
    368 				} else {
    369 					free(state->token->key);
    370 					free(state->token);
    371 					state->token = NULL;
    372 					return NULL;
    373 				}
    374 			} else {
    375 				state->token->realm = NULL;
    376 				state->token->realm_len = 0;
    377 			}
    378 		}
    379 		/* If we cannot save the token, we must invalidate */
    380 		if (state->token == NULL)
    381 			return NULL;
    382 	}
    383 
    384 	return t;
    385 }
    386 
    387 static uint64_t
    388 get_next_rdm_monotonic_counter(struct auth *auth)
    389 {
    390 	FILE *fp;
    391 	uint64_t rdm;
    392 #ifdef LOCK_EX
    393 	int flocked;
    394 #endif
    395 
    396 	fp = fopen(RDM_MONOFILE, "r+");
    397 	if (fp == NULL) {
    398 		if (errno != ENOENT)
    399 			return ++auth->last_replay; /* report error? */
    400 		fp = fopen(RDM_MONOFILE, "w");
    401 		if (fp == NULL)
    402 			return ++auth->last_replay; /* report error? */
    403 #ifdef LOCK_EX
    404 		flocked = flock(fileno(fp), LOCK_EX);
    405 #endif
    406 		rdm = 0;
    407 	} else {
    408 #ifdef LOCK_EX
    409 		flocked = flock(fileno(fp), LOCK_EX);
    410 #endif
    411 		if (fscanf(fp, "0x%016" PRIu64, &rdm) != 1)
    412 			rdm = 0; /* truncated? report error? */
    413 	}
    414 
    415 	rdm++;
    416 	if (fseek(fp, 0, SEEK_SET) == -1 ||
    417 	    ftruncate(fileno(fp), 0) == -1 ||
    418 	    fprintf(fp, "0x%016" PRIu64 "\n", rdm) != 19)
    419 	{
    420 		if (!auth->last_replay_set) {
    421 			auth->last_replay = rdm;
    422 			auth->last_replay_set = 1;
    423 		} else
    424 			rdm = ++auth->last_replay;
    425 		/* report error? */
    426 	}
    427 	fflush(fp);
    428 #ifdef LOCK_EX
    429 	if (flocked == 0)
    430 		flock(fileno(fp), LOCK_UN);
    431 #endif
    432 	fclose(fp);
    433 	return rdm;
    434 }
    435 
    436 #define JAN_1970       2208988800U    /* 1970 - 1900 in seconds */
    437 static uint64_t
    438 get_next_rdm_monotonic_clock(struct auth *auth)
    439 {
    440 	struct timespec ts;
    441 	uint32_t pack[2];
    442 	double frac;
    443 	uint64_t rdm;
    444 
    445 	if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
    446 		return ++auth->last_replay; /* report error? */
    447 	pack[0] = htonl((uint32_t)ts.tv_sec + JAN_1970);
    448 	frac = ((double)ts.tv_nsec / 1e9 * 0x100000000ULL);
    449 	pack[1] = htonl((uint32_t)frac);
    450 
    451 	memcpy(&rdm, &pack, sizeof(rdm));
    452 	return rdm;
    453 }
    454 
    455 static uint64_t
    456 get_next_rdm_monotonic(struct auth *auth)
    457 {
    458 
    459 	if (auth->options & DHCPCD_AUTH_RDM_COUNTER)
    460 		return get_next_rdm_monotonic_counter(auth);
    461 	return get_next_rdm_monotonic_clock(auth);
    462 }
    463 
    464 /*
    465  * Encode a DHCP message.
    466  * Either we know which token to use from the server response
    467  * or we are using a basic configuration token.
    468  * token is the token to encrypt with.
    469  * m and mlen refer to the whole message.
    470  * mp is the DHCP type, pass it 4 or 6.
    471  * mt is the DHCP message type.
    472  * data and dlen refer to the authentication option within the message.
    473  */
    474 ssize_t
    475 dhcp_auth_encode(struct auth *auth, const struct token *t,
    476     uint8_t *m, size_t mlen, int mp, int mt,
    477     uint8_t *data, size_t dlen)
    478 {
    479 	uint64_t rdm;
    480 	uint8_t hmac[HMAC_LENGTH];
    481 	time_t now;
    482 	uint8_t hops, *p, info;
    483 	uint32_t giaddr, secretid;
    484 
    485 	if (auth->protocol == 0 && t == NULL) {
    486 		TAILQ_FOREACH(t, &auth->tokens, next) {
    487 			if (t->secretid == 0 &&
    488 			    t->realm_len == 0)
    489 			break;
    490 		}
    491 		if (t == NULL) {
    492 			errno = EINVAL;
    493 			return -1;
    494 		}
    495 		if (t->expire) {
    496 			if (time(&now) == -1)
    497 				return -1;
    498 			if (t->expire < now) {
    499 				errno = EPERM;
    500 				return -1;
    501 			}
    502 		}
    503 	}
    504 
    505 	switch(auth->protocol) {
    506 	case AUTH_PROTO_TOKEN:
    507 	case AUTH_PROTO_DELAYED:
    508 	case AUTH_PROTO_DELAYEDREALM:
    509 		/* We don't ever send a reconf key */
    510 		break;
    511 	default:
    512 		errno = ENOTSUP;
    513 		return -1;
    514 	}
    515 
    516 	switch(auth->algorithm) {
    517 	case AUTH_ALG_HMAC_MD5:
    518 		break;
    519 	default:
    520 		errno = ENOTSUP;
    521 		return -1;
    522 	}
    523 
    524 	switch(auth->rdm) {
    525 	case AUTH_RDM_MONOTONIC:
    526 		break;
    527 	default:
    528 		errno = ENOTSUP;
    529 		return -1;
    530 	}
    531 
    532 	/* DISCOVER or INFORM messages don't write auth info */
    533 	if ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||
    534 	    (mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))
    535 		info = 0;
    536 	else
    537 		info = 1;
    538 
    539 	/* Work out the auth area size.
    540 	 * We only need to do this for DISCOVER messages */
    541 	if (data == NULL) {
    542 		dlen = 1 + 1 + 1 + 8;
    543 		switch(auth->protocol) {
    544 		case AUTH_PROTO_TOKEN:
    545 			dlen += t->key_len;
    546 			break;
    547 		case AUTH_PROTO_DELAYEDREALM:
    548 			if (info && t)
    549 				dlen += t->realm_len;
    550 			/* FALLTHROUGH */
    551 		case AUTH_PROTO_DELAYED:
    552 			if (info && t)
    553 				dlen += sizeof(t->secretid) + sizeof(hmac);
    554 			break;
    555 		}
    556 		return (ssize_t)dlen;
    557 	}
    558 
    559 	if (dlen < 1 + 1 + 1 + 8) {
    560 		errno = ENOBUFS;
    561 		return -1;
    562 	}
    563 
    564 	/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
    565 	if (data < m || data > m + mlen || data + dlen > m + mlen) {
    566 		errno = ERANGE;
    567 		return -1;
    568 	}
    569 
    570 	/* Write out our option */
    571 	*data++ = auth->protocol;
    572 	*data++ = auth->algorithm;
    573 	*data++ = auth->rdm;
    574 	switch (auth->rdm) {
    575 	case AUTH_RDM_MONOTONIC:
    576 		rdm = get_next_rdm_monotonic(auth);
    577 		break;
    578 	default:
    579 		/* This block appeases gcc, clang doesn't need it */
    580 		rdm = get_next_rdm_monotonic(auth);
    581 		break;
    582 	}
    583 	rdm = htonll(rdm);
    584 	memcpy(data, &rdm, 8);
    585 	data += 8;
    586 	dlen -= 1 + 1 + 1 + 8;
    587 
    588 	/* Special case as no hashing needs to be done. */
    589 	if (auth->protocol == AUTH_PROTO_TOKEN) {
    590 		/* Should be impossible, but still */
    591 		if (t == NULL) {
    592 			errno = EINVAL;
    593 			return -1;
    594 		}
    595 		if (dlen < t->key_len) {
    596 			errno =	ENOBUFS;
    597 			return -1;
    598 		}
    599 		memcpy(data, t->key, t->key_len);
    600 		return (ssize_t)(dlen - t->key_len);
    601 	}
    602 
    603 	/* DISCOVER or INFORM messages don't write auth info */
    604 	if (!info)
    605 		return (ssize_t)dlen;
    606 
    607 	/* Loading a saved lease without an authentication option */
    608 	if (t == NULL)
    609 		return 0;
    610 
    611 	/* Write out the Realm */
    612 	if (auth->protocol == AUTH_PROTO_DELAYEDREALM) {
    613 		if (dlen < t->realm_len) {
    614 			errno = ENOBUFS;
    615 			return -1;
    616 		}
    617 		memcpy(data, t->realm, t->realm_len);
    618 		data += t->realm_len;
    619 		dlen -= t->realm_len;
    620 	}
    621 
    622 	/* Write out the SecretID */
    623 	if (auth->protocol == AUTH_PROTO_DELAYED ||
    624 	    auth->protocol == AUTH_PROTO_DELAYEDREALM)
    625 	{
    626 		if (dlen < sizeof(t->secretid)) {
    627 			errno = ENOBUFS;
    628 			return -1;
    629 		}
    630 		secretid = htonl(t->secretid);
    631 		memcpy(data, &secretid, sizeof(secretid));
    632 		data += sizeof(secretid);
    633 		dlen -= sizeof(secretid);
    634 	}
    635 
    636 	/* Zero what's left, the MAC */
    637 	memset(data, 0, dlen);
    638 
    639 	/* RFC3318, section 5.2 - zero giaddr and hops */
    640 	if (mp == 4) {
    641 		p = m + offsetof(struct dhcp_message, hwopcount);
    642 		hops = *p;
    643 		*p = '\0';
    644 		p = m + offsetof(struct dhcp_message, giaddr);
    645 		memcpy(&giaddr, p, sizeof(giaddr));
    646 		memset(p, 0, sizeof(giaddr));
    647 	} else {
    648 		/* appease GCC again */
    649 		hops = 0;
    650 		giaddr = 0;
    651 	}
    652 
    653 	/* Create our hash and write it out */
    654 	switch(auth->algorithm) {
    655 	case AUTH_ALG_HMAC_MD5:
    656 		hmac_md5(m, mlen, t->key, t->key_len, hmac);
    657 		memcpy(data, hmac, sizeof(hmac));
    658 		break;
    659 	}
    660 
    661 	/* RFC3318, section 5.2 - restore giaddr and hops */
    662 	if (mp == 4) {
    663 		p = m + offsetof(struct dhcp_message, hwopcount);
    664 		*p = hops;
    665 		p = m + offsetof(struct dhcp_message, giaddr);
    666 		memcpy(p, &giaddr, sizeof(giaddr));
    667 	}
    668 
    669 	/* Done! */
    670 	return (int)(dlen - sizeof(hmac)); /* should be zero */
    671 }
    672