mirror of
https://git.savannah.nongnu.org/git/lwip.git
synced 2026-06-16 09:53:45 +08:00
nd6: Parse options instead of only using first
When parsing Neighbor Solicitation, Neighbor Advertisement and Redirect messages, options will be searched through for the expected link-layer address type instead of just using the first option.
This commit is contained in:
@@ -292,6 +292,37 @@ nd6_process_autoconfig_prefix(struct netif *netif,
|
||||
}
|
||||
#endif /* LWIP_IPV6_AUTOCONFIG */
|
||||
|
||||
/**
|
||||
* Return a pointer to the first instance of a specific ICMP option in a pbuf,
|
||||
* starting from a specific offset.
|
||||
*
|
||||
* @param p the nd packet, p->payload pointing to the icmpv6 header
|
||||
* @param offset the byte offset for the start of icmpv6 options
|
||||
* @param wanted_type the option type to search for
|
||||
* @return A pointer into p->payload where the option starts,
|
||||
* or NULL if option is not found, option was not fully inside p->payload,
|
||||
* or a zero-length option was found.
|
||||
*/
|
||||
static void *
|
||||
nd6_find_option(struct pbuf *p, int offset, int wanted_type)
|
||||
{
|
||||
while ((p->tot_len - offset) >= 2) {
|
||||
u8_t option_type;
|
||||
u16_t option_len;
|
||||
int option_len8 = pbuf_try_get_at(p, offset + 1);
|
||||
if (option_len8 <= 0) {
|
||||
break;
|
||||
}
|
||||
option_len = ((u8_t)option_len8) << 3;
|
||||
option_type = pbuf_get_at(p, offset);
|
||||
if (p->len >= (offset + option_len) && option_type == wanted_type) {
|
||||
return ((u8_t*)p->payload + offset);
|
||||
}
|
||||
offset += option_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an incoming neighbor discovery message
|
||||
*
|
||||
@@ -363,19 +394,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
}
|
||||
#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
|
||||
|
||||
/* Check that link-layer address option also fits in packet. */
|
||||
if (p->len < (sizeof(struct na_header) + 2)) {
|
||||
/* @todo debug message */
|
||||
pbuf_free(p);
|
||||
ND6_STATS_INC(nd6.lenerr);
|
||||
ND6_STATS_INC(nd6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
|
||||
|
||||
if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
|
||||
/* @todo debug message */
|
||||
lladdr_opt = nd6_find_option(p, sizeof(*na_hdr), ND6_OPTION_TYPE_TARGET_LLADDR);
|
||||
if (!lladdr_opt) {
|
||||
/* Missing target lladdr option, or did not fully fit inside p */
|
||||
pbuf_free(p);
|
||||
ND6_STATS_INC(nd6.lenerr);
|
||||
ND6_STATS_INC(nd6.drop);
|
||||
@@ -405,19 +426,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
/* Update cache entry. */
|
||||
if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
|
||||
(neighbor_cache[i].state == ND6_INCOMPLETE)) {
|
||||
/* Check that link-layer address option also fits in packet. */
|
||||
if (p->len < (sizeof(struct na_header) + 2)) {
|
||||
/* @todo debug message */
|
||||
pbuf_free(p);
|
||||
ND6_STATS_INC(nd6.lenerr);
|
||||
ND6_STATS_INC(nd6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
|
||||
|
||||
if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
|
||||
/* @todo debug message */
|
||||
lladdr_opt = nd6_find_option(p, sizeof(*na_hdr), ND6_OPTION_TYPE_TARGET_LLADDR);
|
||||
if (!lladdr_opt) {
|
||||
/* Missing target lladdr option, or did not fully fit inside p */
|
||||
pbuf_free(p);
|
||||
ND6_STATS_INC(nd6.lenerr);
|
||||
ND6_STATS_INC(nd6.drop);
|
||||
@@ -475,14 +486,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
/* @todo RFC MUST: if IP source is 'any', there is no source LL address option */
|
||||
|
||||
/* Check if there is a link-layer address provided. Only point to it if in this buffer. */
|
||||
if (p->len >= (sizeof(struct ns_header) + 2)) {
|
||||
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
|
||||
if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) {
|
||||
lladdr_opt = NULL;
|
||||
}
|
||||
} else {
|
||||
lladdr_opt = NULL;
|
||||
}
|
||||
lladdr_opt = nd6_find_option(p, sizeof(*ns_hdr), ND6_OPTION_TYPE_SOURCE_LLADDR);
|
||||
|
||||
/* Check if the target address is configured on the receiving netif. */
|
||||
accepted = 0;
|
||||
@@ -853,14 +857,8 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
/* @todo RFC MUST: ICMP target address is either link-local address or same as destination_address */
|
||||
/* @todo RFC MUST: all included options have a length greater than zero */
|
||||
|
||||
if (p->len >= (sizeof(struct redirect_header) + 2)) {
|
||||
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
|
||||
if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
|
||||
lladdr_opt = NULL;
|
||||
}
|
||||
} else {
|
||||
lladdr_opt = NULL;
|
||||
}
|
||||
/* Check if there is a link-layer address provided. Only point to it if in this buffer. */
|
||||
lladdr_opt = nd6_find_option(p, sizeof(*redir_hdr), ND6_OPTION_TYPE_TARGET_LLADDR);
|
||||
|
||||
/* Find dest address in cache */
|
||||
dest_idx = nd6_find_destination_cache_entry(&destination_address);
|
||||
@@ -879,7 +877,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
|
||||
/* If Link-layer address of other router is given, try to add to neighbor cache. */
|
||||
if (lladdr_opt != NULL) {
|
||||
if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
|
||||
i = nd6_find_neighbor_cache_entry(&target_address);
|
||||
if (i < 0) {
|
||||
i = nd6_new_neighbor_cache_entry();
|
||||
@@ -904,7 +901,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break; /* ICMP6_TYPE_RD */
|
||||
}
|
||||
case ICMP6_TYPE_PTB: /* Packet too big */
|
||||
|
||||
Reference in New Issue
Block a user