summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel-pppd/ipv6/dhcpv6.c18
-rw-r--r--accel-pppd/ipv6/dhcpv6_packet.c111
2 files changed, 79 insertions, 50 deletions
diff --git a/accel-pppd/ipv6/dhcpv6.c b/accel-pppd/ipv6/dhcpv6.c
index 4a80bdb9..1d565a76 100644
--- a/accel-pppd/ipv6/dhcpv6.c
+++ b/accel-pppd/ipv6/dhcpv6.c
@@ -222,25 +222,29 @@ static void insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
{
struct dhcpv6_option *opt1;
int i, j, dns_count;
- uint16_t *ptr;
- struct in6_addr addr, *addr_ptr;
+ uint8_t *ptr;
+ uint16_t code;
+ struct in6_addr addr;
+ uint8_t *addr_ptr;
struct in6_addr dns[MAX_DNS_COUNT];
- for (i = ntohs(opt->hdr->len) / 2, ptr = (uint16_t *)opt->hdr->data; i; i--, ptr++) {
- if (ntohs(*ptr) == D6_OPTION_DNS_SERVERS) {
+ for (i = ntohs(opt->hdr->len) / 2, ptr = opt->hdr->data; i; i--, ptr += sizeof(code)) {
+ memcpy(&code, ptr, sizeof(code));
+ code = ntohs(code);
+ if (code == D6_OPTION_DNS_SERVERS) {
dns_count = ipv6_dns_get(reply->ses, conf_dns, conf_dns_count,
dns, MAX_DNS_COUNT);
if (dns_count) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, dns_count * sizeof(addr));
- for (j = 0, addr_ptr = (struct in6_addr *)opt1->hdr->data; j < dns_count; j++, addr_ptr++)
+ for (j = 0, addr_ptr = opt1->hdr->data; j < dns_count; j++, addr_ptr += sizeof(addr))
memcpy(addr_ptr, dns + j, sizeof(addr));
}
- } else if (ntohs(*ptr) == D6_OPTION_DOMAIN_LIST) {
+ } else if (code == D6_OPTION_DOMAIN_LIST) {
if (conf_dnssl_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DOMAIN_LIST, conf_dnssl_size);
memcpy(opt1->hdr->data, conf_dnssl, conf_dnssl_size);
}
- } else if (ntohs(*ptr) == D6_OPTION_AFTR_NAME) {
+ } else if (code == D6_OPTION_AFTR_NAME) {
if (conf_aftr_gw_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_AFTR_NAME, conf_aftr_gw_size);
memcpy(opt1->hdr->data, conf_aftr_gw, conf_aftr_gw_size);
diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c
index c0c7bc81..1bf297af 100644
--- a/accel-pppd/ipv6/dhcpv6_packet.c
+++ b/accel-pppd/ipv6/dhcpv6_packet.c
@@ -13,7 +13,8 @@ struct dict_option {
int code;
const char *name;
int recv;
- int len;
+ int min_len;
+ int nested;
void (*print)(struct dhcpv6_option *, void (*)(const char *fmt, ...));
};
@@ -33,31 +34,39 @@ static void print_dnssl(struct dhcpv6_option *opt, void (*print)(const char *fmt
static void print_ia_prefix(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
static void print_aftr_gw(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static uint16_t dhcpv6_read_u16(const void *ptr)
+{
+ uint16_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return ntohs(value);
+}
+
static struct dict_option known_options[] = {
- { D6_OPTION_CLIENTID, "Client-ID", 1, 0, print_clientid },
- { D6_OPTION_SERVERID, "Server-ID", 0, 0, print_clientid },
- { D6_OPTION_IA_NA, "IA-NA", 1, sizeof(struct dhcpv6_opt_ia_na), print_ia_na },
- { D6_OPTION_IA_TA, "IA-TA", 1, sizeof(struct dhcpv6_opt_ia_ta), print_ia_ta },
- { D6_OPTION_IAADDR, "IA-Addr", 1, sizeof(struct dhcpv6_opt_ia_addr), print_ia_addr },
- { D6_OPTION_ORO, "Option-Request", 1, 0, print_oro },
- { D6_OPTION_PREFERENCE, "Preference", 0, 0, print_uint8 },
- { D6_OPTION_ELAPSED_TIME, "Elapsed-Time", 1, 0, print_time },
- { D6_OPTION_RELAY_MSG, "Relay-Message", 1, 0 },
- { D6_OPTION_AUTH, "Auth", 1, 0 },
- { D6_OPTION_PREFERENCE, "Server-Unicast", 0, 0, print_ipv6addr },
- { D6_OPTION_STATUS_CODE, "Status", 0, 0, print_status },
- { D6_OPTION_RAPID_COMMIT, "Rapid-Commit", 1, 0 },
- { D6_OPTION_USER_CLASS, "User-Class", 1, 0 },
- { D6_OPTION_VENDOR_CLASS, "Vendor-Class", 1, 0, print_hex_array },
- { D6_OPTION_VENDOR_SPECIFIC, "Vendor-Specific", 1, 0, print_hex_array },
- { D6_OPTION_INTERFACE_ID, "Interface-ID", 1, 0, print_hex_array },
- { D6_OPTION_RECONF_MSG, "Reconfigure", 0, 0, print_reconf },
- { D6_OPTION_RECONF_ACCEPT, "Reconfigure-Accept", 1, 0 },
- { D6_OPTION_DNS_SERVERS, "DNS", 1, 0, print_ipv6addr_array },
- { D6_OPTION_DOMAIN_LIST, "DNSSL", 1, 0, print_dnssl },
- { D6_OPTION_IA_PD, "IA-PD", 1, sizeof(struct dhcpv6_opt_ia_na), print_ia_na },
- { D6_OPTION_IAPREFIX, "IA-Prefix", 1, sizeof(struct dhcpv6_opt_ia_prefix), print_ia_prefix },
- { D6_OPTION_AFTR_NAME, "AFTR-Name", 1, 0, print_aftr_gw },
+ { D6_OPTION_CLIENTID, "Client-ID", 1, sizeof(uint16_t), 0, print_clientid },
+ { D6_OPTION_SERVERID, "Server-ID", 0, sizeof(uint16_t), 0, print_clientid },
+ { D6_OPTION_IA_NA, "IA-NA", 1, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_na },
+ { D6_OPTION_IA_TA, "IA-TA", 1, sizeof(struct dhcpv6_opt_ia_ta) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_ta },
+ { D6_OPTION_IAADDR, "IA-Addr", 1, sizeof(struct dhcpv6_opt_ia_addr) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_addr },
+ { D6_OPTION_ORO, "Option-Request", 1, 0, 0, print_oro },
+ { D6_OPTION_PREFERENCE, "Preference", 0, sizeof(uint8_t), 0, print_uint8 },
+ { D6_OPTION_ELAPSED_TIME, "Elapsed-Time", 1, sizeof(uint16_t), 0, print_time },
+ { D6_OPTION_RELAY_MSG, "Relay-Message", 1, sizeof(struct dhcpv6_msg_hdr), 0 },
+ { D6_OPTION_AUTH, "Auth", 1, 0, 0 },
+ { D6_OPTION_UNICAST, "Server-Unicast", 0, sizeof(struct in6_addr), 0, print_ipv6addr },
+ { D6_OPTION_STATUS_CODE, "Status", 0, sizeof(uint16_t), 0, print_status },
+ { D6_OPTION_RAPID_COMMIT, "Rapid-Commit", 1, 0, 0 },
+ { D6_OPTION_USER_CLASS, "User-Class", 1, 0, 0 },
+ { D6_OPTION_VENDOR_CLASS, "Vendor-Class", 1, 0, 0, print_hex_array },
+ { D6_OPTION_VENDOR_SPECIFIC, "Vendor-Specific", 1, 0, 0, print_hex_array },
+ { D6_OPTION_INTERFACE_ID, "Interface-ID", 1, 0, 0, print_hex_array },
+ { D6_OPTION_RECONF_MSG, "Reconfigure", 0, sizeof(uint8_t), 0, print_reconf },
+ { D6_OPTION_RECONF_ACCEPT, "Reconfigure-Accept", 1, 0, 0 },
+ { D6_OPTION_DNS_SERVERS, "DNS", 1, 0, 0, print_ipv6addr_array },
+ { D6_OPTION_DOMAIN_LIST, "DNSSL", 1, 0, 0, print_dnssl },
+ { D6_OPTION_IA_PD, "IA-PD", 1, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_na },
+ { D6_OPTION_IAPREFIX, "IA-Prefix", 1, sizeof(struct dhcpv6_opt_ia_prefix) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_prefix },
+ { D6_OPTION_AFTR_NAME, "AFTR-Name", 1, 0, 0, print_aftr_gw },
{ 0 }
};
@@ -89,9 +98,14 @@ static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
break;
}
- if (dopt->len) {
+ if (ntohs(opth->len) < dopt->min_len) {
+ log_warn("dhcpv6: invalid packet received\n");
+ return NULL;
+ }
+
+ if (dopt->nested) {
endptr = ptr + sizeof(*opth) + ntohs(opth->len);
- ptr += dopt->len;
+ ptr += sizeof(*opth) + dopt->min_len;
while (ptr < endptr) {
ptr = parse_option(ptr, endptr, &opt->opt_list);
if (!ptr)
@@ -109,7 +123,8 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
struct dhcpv6_opt_hdr *opth;
struct dhcpv6_relay *rel;
struct dhcpv6_relay_hdr *rhdr;
- void *ptr, *endptr;
+ struct dhcpv6_msg_hdr *inner_hdr;
+ void *ptr, *endptr, *relay_end, *inner_end;
if (size < sizeof(struct dhcpv6_msg_hdr)) {
if (conf_verbose)
@@ -133,8 +148,6 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
endptr = ((void *)pkt->hdr) + size;
while (pkt->hdr->type == D6_RELAY_FORW) {
- struct dhcpv6_msg_hdr *prev_hdr = pkt->hdr;
-
rhdr = (struct dhcpv6_relay_hdr *)pkt->hdr;
if (((void *)rhdr) + sizeof(*rhdr) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
@@ -153,27 +166,37 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
list_add_tail(&rel->entry, &pkt->relay_list);
+ inner_hdr = NULL;
+ inner_end = NULL;
+ relay_end = endptr;
ptr = rhdr->data;
- while (ptr < endptr) {
+ while (ptr < relay_end) {
opth = ptr;
- if (ptr + sizeof(*opth) > endptr ||
- ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
+ if (ptr + sizeof(*opth) > relay_end ||
+ ptr + sizeof(*opth) + ntohs(opth->len) > relay_end) {
log_warn("dhcpv6: invalid packet received\n");
goto error;
}
if (opth->code == htons(D6_OPTION_RELAY_MSG)) {
- pkt->hdr = (struct dhcpv6_msg_hdr *)opth->data;
- endptr = opth->data + ntohs(opth->len);
+ if (inner_hdr || ntohs(opth->len) < sizeof(*inner_hdr)) {
+ log_warn("dhcpv6: invalid packet received\n");
+ goto error;
+ }
+ inner_hdr = (struct dhcpv6_msg_hdr *)opth->data;
+ inner_end = opth->data + ntohs(opth->len);
}
ptr += sizeof(*opth) + ntohs(opth->len);
}
- if (pkt->hdr == prev_hdr) {
+ if (!inner_hdr) {
log_warn("dhcpv6: invalid packet received\n");
goto error;
}
+
+ pkt->hdr = inner_hdr;
+ endptr = inner_end;
}
ptr = pkt->hdr->data;
@@ -462,26 +485,28 @@ static void print_ia_addr(struct dhcpv6_option *opt, void (*print)(const char *f
static void print_oro(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
{
- uint16_t *ptr = (uint16_t *)opt->hdr->data;
- uint16_t *end_ptr = ptr + ntohs(opt->hdr->len)/2;
+ uint8_t *ptr = opt->hdr->data;
+ uint8_t *end_ptr = ptr + ntohs(opt->hdr->len) / 2 * sizeof(uint16_t);
struct dict_option *dopt;
+ uint16_t code;
int f = 0;
- for (; ptr < end_ptr; ptr++) {
+ for (; ptr < end_ptr; ptr += sizeof(uint16_t)) {
if (f)
print(",");
else
print(" ");
+ code = dhcpv6_read_u16(ptr);
for (dopt = known_options; dopt->code; dopt++) {
- if (ntohs(*ptr) == dopt->code)
+ if (code == dopt->code)
break;
}
if (dopt->code)
print("%s", dopt->name);
else
- print("%i", ntohs(*ptr));
+ print("%i", code);
f = 1;
}
@@ -529,9 +554,9 @@ static void print_ipv6addr_array(struct dhcpv6_option *opt, void (*print)(const
char str[INET6_ADDRSTRLEN];
int i;
int f = 0;
- struct in6_addr *addr = (struct in6_addr *)opt->hdr->data;
+ uint8_t *addr = opt->hdr->data;
- for (i = ntohs(opt->hdr->len) / sizeof(*addr); i; i--, addr++) {
+ for (i = ntohs(opt->hdr->len) / sizeof(struct in6_addr); i; i--, addr += sizeof(struct in6_addr)) {
inet_ntop(AF_INET6, addr, str, sizeof(str));
print("%c%s", f ? ',' : ' ', str);
f = 1;