X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fdhcp6-option.c;h=ea863f45e4ea13a400461949a62cbce71c913379;hb=5f86c1f4c43ee9caa120d130e9b89d3fd25124c0;hp=f488832cf919ca4406c3a192ee124b9b05cb8323;hpb=c6affce8740bb0cee42eebf6d1d44dd518035e88;p=elogind.git diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f488832cf..ea863f45e 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -24,32 +24,37 @@ #include #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; } @@ -58,13 +63,14 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval) { int r; - assert_return(optval, -EINVAL); + assert_return(optval || optlen == 0, -EINVAL); r = option_append_hdr(buf, buflen, code, optlen); if (r < 0) return r; - memcpy(*buf, optval, optlen); + if (optval) + memcpy(*buf, optval, optlen); *buf += optlen; *buflen -= optlen; @@ -100,8 +106,8 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { 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); @@ -110,16 +116,16 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { 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); @@ -130,23 +136,23 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { } -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; @@ -190,8 +196,8 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, 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; } @@ -212,8 +218,8 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, 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; } @@ -249,10 +255,10 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, 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",