#include <string.h>
#include "sparse-endian.h"
+#include "unaligned.h"
#include "util.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
-#define DHCP6_OPTION_HDR_LEN 4
#define DHCP6_OPTION_IA_NA_LEN 12
#define DHCP6_OPTION_IA_TA_LEN 4
-#define DHCP6_OPTION_IAADDR_LEN 24
+
+typedef struct DHCP6Option {
+ be16_t code;
+ be16_t len;
+ uint8_t data[];
+} _packed_ DHCP6Option;
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
+ DHCP6Option *option = (DHCP6Option*) *buf;
+
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
- if (optlen > 0xffff || *buflen < optlen + DHCP6_OPTION_HDR_LEN)
+ if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option))
return -ENOBUFS;
- (*buf)[0] = optcode >> 8;
- (*buf)[1] = optcode & 0xff;
- (*buf)[2] = optlen >> 8;
- (*buf)[3] = optlen & 0xff;
+ option->code = htobe16(optcode);
+ option->len = htobe16(optlen);
- *buf += DHCP6_OPTION_HDR_LEN;
- *buflen -= DHCP6_OPTION_HDR_LEN;
+ *buf += sizeof(DHCP6Option);
+ *buflen -= sizeof(DHCP6Option);
return 0;
}
ia_hdr = *buf;
ia_buflen = *buflen;
- *buf += DHCP6_OPTION_HDR_LEN;
- *buflen -= DHCP6_OPTION_HDR_LEN;
+ *buf += sizeof(DHCP6Option);
+ *buflen -= sizeof(DHCP6Option);
memcpy(*buf, &ia->id, len);
LIST_FOREACH(addresses, addr, ia->addresses) {
r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
- DHCP6_OPTION_IAADDR_LEN);
+ sizeof(addr->iaaddr));
if (r < 0)
return r;
- memcpy(*buf, &addr->address, DHCP6_OPTION_IAADDR_LEN);
+ memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
- *buf += DHCP6_OPTION_IAADDR_LEN;
- *buflen -= DHCP6_OPTION_IAADDR_LEN;
+ *buf += sizeof(addr->iaaddr);
+ *buflen -= sizeof(addr->iaaddr);
- ia_addrlen += DHCP6_OPTION_HDR_LEN + DHCP6_OPTION_IAADDR_LEN;
+ ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
}
r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
}
-static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *opt,
- size_t *optlen) {
+static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
+ DHCP6Option *option = (DHCP6Option*) *buf;
uint16_t len;
assert_return(buf, -EINVAL);
- assert_return(opt, -EINVAL);
+ assert_return(optcode, -EINVAL);
assert_return(optlen, -EINVAL);
- if (*buflen < 4)
+ if (*buflen < sizeof(DHCP6Option))
return -ENOMSG;
- len = (*buf)[2] << 8 | (*buf)[3];
+ len = be16toh(option->len);
if (len > *buflen)
return -ENOMSG;
- *opt = (*buf)[0] << 8 | (*buf)[1];
+ *optcode = be16toh(option->code);
*optlen = len;
*buf += 4;
switch (iatype) {
case DHCP6_OPTION_IA_NA:
- if (*buflen < DHCP6_OPTION_IA_NA_LEN + DHCP6_OPTION_HDR_LEN +
- DHCP6_OPTION_IAADDR_LEN) {
+ if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
+ sizeof(addr->iaaddr)) {
r = -ENOBUFS;
goto error;
}
break;
case DHCP6_OPTION_IA_TA:
- if (*buflen < DHCP6_OPTION_IA_TA_LEN + DHCP6_OPTION_HDR_LEN +
- DHCP6_OPTION_IAADDR_LEN) {
+ if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
+ sizeof(addr->iaaddr)) {
r = -ENOBUFS;
goto error;
}
LIST_INIT(addresses, addr);
- memcpy(&addr->address, *buf, DHCP6_OPTION_IAADDR_LEN);
+ memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
- lt_valid = be32toh(addr->lifetime_valid);
- lt_pref = be32toh(addr->lifetime_valid);
+ lt_valid = be32toh(addr->iaaddr.lifetime_valid);
+ lt_pref = be32toh(addr->iaaddr.lifetime_valid);
if (!lt_valid || lt_pref > lt_valid) {
log_dhcp6_client(client, "IA preferred %ds > valid %ds",