net: Initial IPv6 neighbor discovery support
Change-Id: I76dc7471c56085a61bbdd9f75ecb49aeb0874dbb Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
a7e75ba81b
commit
9b3d8378e5
|
@ -101,6 +101,10 @@ struct net_nbuf {
|
|||
/* Filled by layer 2 when network packet is received. */
|
||||
struct net_linkaddr lladdr_src;
|
||||
struct net_linkaddr lladdr_dst;
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
uint8_t ext_opt_len; /* IPv6 ND option length */
|
||||
#endif
|
||||
/* @endcond */
|
||||
|
||||
/** Network connection context */
|
||||
|
@ -181,6 +185,8 @@ static inline void net_nbuf_ll_swap(struct net_buf *buf)
|
|||
(((struct net_nbuf *)net_buf_user_data((buf)))->ext_bitmap)
|
||||
#define net_nbuf_next_hdr(buf) \
|
||||
(((struct net_nbuf *)net_buf_user_data((buf)))->next_hdr)
|
||||
#define net_nbuf_ext_opt_len(buf) \
|
||||
(((struct net_nbuf *)net_buf_user_data((buf)))->ext_opt_len)
|
||||
|
||||
#define NET_IPV6_BUF(buf) ((struct net_ipv6_hdr *)net_nbuf_ip_data(buf))
|
||||
#define NET_IPV4_BUF(buf) ((struct net_ipv4_hdr *)net_nbuf_ip_data(buf))
|
||||
|
|
|
@ -133,6 +133,12 @@ struct net_stats_udp {
|
|||
net_stats_t chkerr;
|
||||
};
|
||||
|
||||
struct net_stats_ipv6_nd {
|
||||
net_stats_t drop;
|
||||
net_stats_t recv;
|
||||
net_stats_t sent;
|
||||
};
|
||||
|
||||
struct net_stats {
|
||||
net_stats_t processing_error;
|
||||
|
||||
|
@ -155,6 +161,10 @@ struct net_stats {
|
|||
#if defined (CONFIG_NET_UDP)
|
||||
struct net_stats_udp udp;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
struct net_stats_ipv6_nd ipv6_nd;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define __NET_IF_H__
|
||||
|
||||
#include <device.h>
|
||||
#include <misc/nano_work.h>
|
||||
|
||||
#include <net/net_core.h>
|
||||
#include <net/buf.h>
|
||||
|
@ -59,9 +60,9 @@ struct net_if_addr {
|
|||
/** Timer that triggers renewal */
|
||||
struct nano_timer lifetime;
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
/** Duplicate address detection (DAD) timer */
|
||||
struct nano_timer dad_timer;
|
||||
struct nano_delayed_work dad_timer;
|
||||
|
||||
/** How many times we have done DAD */
|
||||
uint8_t dad_count;
|
||||
|
@ -157,7 +158,13 @@ struct net_if {
|
|||
struct net_if_ipv6_prefix prefix[NET_IF_MAX_IPV6_PREFIX];
|
||||
} ipv6;
|
||||
|
||||
/** IPv6 hop limit */
|
||||
uint8_t hop_limit;
|
||||
|
||||
#if !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
/** IPv6 current duplicate address detection count */
|
||||
uint8_t dad_count;
|
||||
#endif /* !CONFIG_NET_IPV6_NO_DAD */
|
||||
#endif /* CONFIG_NET_IPV6 */
|
||||
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
|
@ -241,6 +248,16 @@ static inline struct net_linkaddr *net_if_get_link_addr(struct net_if *iface)
|
|||
return &iface->link_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start duplicate address detection procedure.
|
||||
* @param iface Pointer to a network interface structure
|
||||
*/
|
||||
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
void net_if_start_dad(struct net_if *iface);
|
||||
#else
|
||||
#define net_if_start_dad(iface)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set a network interfac's link address
|
||||
* @param iface Pointer to a network interface structure
|
||||
|
@ -252,6 +269,11 @@ static inline void net_if_set_link_addr(struct net_if *iface,
|
|||
{
|
||||
iface->link_addr.addr = addr;
|
||||
iface->link_addr.len = len;
|
||||
|
||||
#if !defined(CONFIG_NET_NO_DAD)
|
||||
NET_DBG("Starting DAD for iface %p", iface);
|
||||
net_if_start_dad(iface);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,8 @@ menuconfig NETWORKING
|
|||
select NANO_TIMEOUTS
|
||||
select NANO_TIMERS
|
||||
select NET_BUF
|
||||
select SYSTEM_WORKQUEUE
|
||||
select NANO_WORKQUEUE
|
||||
default n
|
||||
help
|
||||
This option enabled generic networking support.
|
||||
|
|
|
@ -60,6 +60,14 @@ config NET_IPV6_MAX_NEIGHBORS
|
|||
help
|
||||
The value depends on your network needs.
|
||||
|
||||
config NET_IPV6_NO_DAD
|
||||
bool "Do not do duplicate address detection"
|
||||
depends on NET_IPV6
|
||||
default n
|
||||
help
|
||||
The value depends on your network needs. DAD should normally
|
||||
be active.
|
||||
|
||||
config NET_IPV4
|
||||
bool "Enable IPv4"
|
||||
default n
|
||||
|
|
|
@ -7,5 +7,5 @@ obj-y = net_core.o \
|
|||
|
||||
obj-y += l2/
|
||||
|
||||
obj-$(CONFIG_NET_IPV6) += icmpv6.o nbr.o
|
||||
obj-$(CONFIG_NET_IPV6) += icmpv6.o nbr.o ipv6.o
|
||||
obj-$(CONFIG_NET_IPV4) += icmpv4.o
|
||||
|
|
|
@ -29,8 +29,52 @@
|
|||
#include <net/net_ip.h>
|
||||
#include <net/nbuf.h>
|
||||
|
||||
struct net_icmpv6_ns_hdr {
|
||||
uint32_t reserved;
|
||||
struct in6_addr tgt;
|
||||
} __packed;
|
||||
|
||||
struct net_icmpv6_nd_opt_hdr {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
} __packed;
|
||||
|
||||
struct net_icmpv6_na_hdr {
|
||||
uint8_t flags;
|
||||
uint8_t reserved[3];
|
||||
struct in6_addr tgt;
|
||||
} __packed;
|
||||
|
||||
#define NET_ICMPV6_NS_BUF(buf) \
|
||||
((struct net_icmpv6_ns_hdr *)(net_nbuf_icmp_data(buf) + \
|
||||
sizeof(struct net_icmp_hdr)))
|
||||
|
||||
#define NET_ICMPV6_ND_OPT_HDR_BUF(buf) \
|
||||
((struct net_icmpv6_nd_opt_hdr *)(net_nbuf_icmp_data(buf) + \
|
||||
sizeof(struct net_icmp_hdr) + \
|
||||
net_nbuf_ext_opt_len(buf)))
|
||||
|
||||
#define NET_ICMPV6_NA_BUF(buf) \
|
||||
((struct net_icmpv6_na_hdr *)(net_nbuf_icmp_data(buf) + \
|
||||
sizeof(struct net_icmp_hdr)))
|
||||
|
||||
#define NET_ICMPV6_ND_OPT_SLLAO 1
|
||||
#define NET_ICMPV6_ND_OPT_TLLAO 2
|
||||
|
||||
#define NET_ICMPV6_OPT_TYPE_OFFSET 0
|
||||
#define NET_ICMPV6_OPT_LEN_OFFSET 1
|
||||
#define NET_ICMPV6_OPT_DATA_OFFSET 2
|
||||
|
||||
#define NET_ICMPV6_NA_FLAG_ROUTER 0x80
|
||||
#define NET_ICMPV6_NA_FLAG_SOLICITED 0x40
|
||||
#define NET_ICMPV6_NA_FLAG_OVERRIDE 0x20
|
||||
#define NET_ICMPV6_RA_FLAG_ONLINK 0x80
|
||||
#define NET_ICMPV6_RA_FLAG_AUTONOMOUS 0x40
|
||||
|
||||
#define NET_ICMPV6_ECHO_REQUEST 128
|
||||
#define NET_ICMPV6_ECHO_REPLY 129
|
||||
#define NET_ICMPV6_NS 135 /* Neighbor Solicitation */
|
||||
#define NET_ICMPV6_NA 136 /* Neighbor Advertisement */
|
||||
|
||||
typedef enum net_verdict (*icmpv6_callback_handler_t)(struct net_buf *buf);
|
||||
|
||||
|
|
869
net/yaip/ipv6.c
Normal file
869
net/yaip/ipv6.c
Normal file
|
@ -0,0 +1,869 @@
|
|||
/** @file
|
||||
* @brief ICMPv6 related functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_NETWORK_IP_STACK_DEBUG_IPV6
|
||||
#define SYS_LOG_DOMAIN "net/ipv6"
|
||||
#define NET_DEBUG 1
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/net_core.h>
|
||||
#include <net/nbuf.h>
|
||||
#include <net/net_stats.h>
|
||||
#include "net_private.h"
|
||||
#include "icmpv6.h"
|
||||
#include "ipv6.h"
|
||||
#include "nbr.h"
|
||||
|
||||
extern void net_neighbor_data_remove(struct net_nbr *nbr);
|
||||
extern void net_neighbor_table_clear(struct net_nbr_table *table);
|
||||
|
||||
enum net_nbr_state {
|
||||
NET_NBR_INCOMPLETE,
|
||||
NET_NBR_REACHABLE,
|
||||
NET_NBR_STALE,
|
||||
NET_NBR_DELAY,
|
||||
NET_NBR_PROBE,
|
||||
};
|
||||
|
||||
struct net_nbr_data {
|
||||
struct in6_addr addr;
|
||||
struct nano_delayed_work reachable;
|
||||
struct nano_delayed_work send_ns;
|
||||
uint8_t ns_count;
|
||||
bool is_router;
|
||||
enum net_nbr_state state;
|
||||
uint16_t link_metric;
|
||||
struct net_buf *pending;
|
||||
};
|
||||
|
||||
NET_NBR_POOL_INIT(net_neighbor_pool, CONFIG_NET_IPV6_MAX_NEIGHBORS,
|
||||
sizeof(struct net_nbr_data), net_neighbor_data_remove);
|
||||
|
||||
NET_NBR_TABLE_INIT(NET_NBR_GLOBAL, neighbor, net_neighbor_pool,
|
||||
net_neighbor_table_clear);
|
||||
|
||||
#define net_nbr_data(nbr) ((struct net_nbr_data *)((nbr)->data))
|
||||
|
||||
#define net_is_solicited(buf) \
|
||||
(NET_ICMPV6_NA_BUF(buf)->flags & NET_ICMPV6_NA_FLAG_SOLICITED)
|
||||
|
||||
#define net_is_router(buf) \
|
||||
(NET_ICMPV6_NA_BUF(buf)->flags & NET_ICMPV6_NA_FLAG_ROUTER)
|
||||
|
||||
#define net_is_override(buf) \
|
||||
(NET_ICMPV6_NA_BUF(buf)->flags & NET_ICMPV6_NA_FLAG_OVERRIDE)
|
||||
|
||||
static struct net_nbr *nbr_lookup(struct net_nbr_table *table,
|
||||
struct net_if *iface,
|
||||
struct in6_addr *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
||||
struct net_nbr *nbr = &table->nbr[i];
|
||||
|
||||
if (!nbr->ref) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nbr->iface == iface &&
|
||||
net_ipv6_addr_cmp(&net_nbr_data(nbr)->addr, addr)) {
|
||||
return nbr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct net_nbr *nbr_add(struct net_buf *buf,
|
||||
struct in6_addr *addr,
|
||||
struct net_linkaddr *lladdr,
|
||||
bool is_router,
|
||||
enum net_nbr_state state)
|
||||
{
|
||||
struct net_nbr *nbr = net_nbr_get(&net_neighbor.table);
|
||||
|
||||
if (!nbr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (net_nbr_link(nbr, net_nbuf_iface(buf), lladdr)) {
|
||||
net_nbr_unref(nbr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
net_ipaddr_copy(&net_nbr_data(nbr)->addr, addr);
|
||||
net_nbr_data(nbr)->state = state;
|
||||
net_nbr_data(nbr)->is_router = is_router;
|
||||
|
||||
return nbr;
|
||||
}
|
||||
|
||||
static struct net_nbr *nbr_new(struct in6_addr *addr,
|
||||
enum net_nbr_state state)
|
||||
{
|
||||
struct net_nbr *nbr = net_nbr_get(&net_neighbor.table);
|
||||
|
||||
if (!nbr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nbr->idx = NET_NBR_LLADDR_UNKNOWN;
|
||||
|
||||
net_ipaddr_copy(&net_nbr_data(nbr)->addr, addr);
|
||||
net_nbr_data(nbr)->state = state;
|
||||
|
||||
return nbr;
|
||||
}
|
||||
|
||||
void net_neighbor_data_remove(struct net_nbr *nbr)
|
||||
{
|
||||
NET_DBG("Neighbor %p removed", nbr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void net_neighbor_table_clear(struct net_nbr_table *table)
|
||||
{
|
||||
NET_DBG("Neighbor table %p cleared", table);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
int net_ipv6_start_dad(struct net_if *iface, struct net_if_addr *ifaddr)
|
||||
{
|
||||
return net_ipv6_send_ns(iface, NULL, NULL, NULL,
|
||||
&ifaddr->address.in6_addr, true);
|
||||
}
|
||||
|
||||
static inline bool dad_failed(struct net_if *iface, struct in6_addr *addr)
|
||||
{
|
||||
if (net_is_ipv6_ll_addr(addr)) {
|
||||
NET_ERR("DAD failed, no ll IPv6 address!");
|
||||
return false;
|
||||
}
|
||||
|
||||
net_if_ipv6_addr_rm(iface, addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif /* !CONFIG_NET_IPV6_NO_DAD */
|
||||
|
||||
#if NET_DEBUG > 0
|
||||
static inline void dbg_update_neighbor_lladdr(struct net_linkaddr *new_lladdr,
|
||||
struct net_linkaddr_storage *old_lladdr,
|
||||
struct in6_addr *addr)
|
||||
{
|
||||
char out[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
|
||||
|
||||
snprintf(out, sizeof(out), net_sprint_ll_addr(old_lladdr->addr,
|
||||
old_lladdr->len));
|
||||
|
||||
NET_DBG("Updating neighbor %s lladdr %s (was %s)",
|
||||
net_sprint_ipv6_addr(addr),
|
||||
net_sprint_ll_addr(new_lladdr->addr, new_lladdr->len),
|
||||
out);
|
||||
}
|
||||
|
||||
static inline void dbg_update_neighbor_lladdr_raw(uint8_t *new_lladdr,
|
||||
struct net_linkaddr_storage *old_lladdr,
|
||||
struct in6_addr *addr)
|
||||
{
|
||||
struct net_linkaddr lladdr = {
|
||||
.len = old_lladdr->len,
|
||||
.addr = new_lladdr,
|
||||
};
|
||||
|
||||
dbg_update_neighbor_lladdr(&lladdr, old_lladdr, addr);
|
||||
}
|
||||
#else
|
||||
#define dbg_update_neighbor_lladdr(...)
|
||||
#define dbg_update_neighbor_lladdr_raw(...)
|
||||
#endif /* NET_DEBUG */
|
||||
|
||||
static inline uint8_t get_llao_len(struct net_if *iface)
|
||||
{
|
||||
if (iface->link_addr.len == 6) {
|
||||
return 8;
|
||||
} else if (iface->link_addr.len == 8) {
|
||||
return 16;
|
||||
}
|
||||
|
||||
/* What else could it be? */
|
||||
NET_ASSERT_INFO(0, "Invalid link address length %d",
|
||||
iface->link_addr.len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void set_llao(struct net_linkaddr *lladdr,
|
||||
uint8_t *llao, uint8_t llao_len, uint8_t type)
|
||||
{
|
||||
llao[NET_ICMPV6_OPT_TYPE_OFFSET] = type;
|
||||
llao[NET_ICMPV6_OPT_LEN_OFFSET] = llao_len >> 3;
|
||||
|
||||
memcpy(&llao[NET_ICMPV6_OPT_DATA_OFFSET], lladdr->addr, lladdr->len);
|
||||
|
||||
memset(&llao[NET_ICMPV6_OPT_DATA_OFFSET + lladdr->len], 0,
|
||||
llao_len - lladdr->len - 2);
|
||||
}
|
||||
|
||||
static void setup_headers(struct net_buf *buf, uint8_t nd6_len,
|
||||
uint8_t icmp_type)
|
||||
{
|
||||
NET_IPV6_BUF(buf)->vtc = 0x60;
|
||||
NET_IPV6_BUF(buf)->tcflow = 0;
|
||||
NET_IPV6_BUF(buf)->flow = 0;
|
||||
NET_IPV6_BUF(buf)->len[0] = 0;
|
||||
NET_IPV6_BUF(buf)->len[1] = NET_ICMPH_LEN + nd6_len;
|
||||
|
||||
NET_IPV6_BUF(buf)->nexthdr = IPPROTO_ICMPV6;
|
||||
NET_IPV6_BUF(buf)->hop_limit = NET_IPV6_ND_HOP_LIMIT;
|
||||
|
||||
NET_ICMP_BUF(buf)->type = icmp_type;
|
||||
NET_ICMP_BUF(buf)->code = 0;
|
||||
}
|
||||
|
||||
static inline void handle_ns_neighbor(struct net_buf *buf,
|
||||
struct net_icmpv6_nd_opt_hdr *hdr)
|
||||
{
|
||||
struct net_nbr *nbr;
|
||||
struct net_linkaddr lladdr = {
|
||||
.len = 8 * hdr->len - 2,
|
||||
.addr = (uint8_t *)hdr + 2,
|
||||
};
|
||||
|
||||
nbr = nbr_lookup(&net_neighbor.table, net_nbuf_iface(buf),
|
||||
&NET_IPV6_BUF(buf)->src);
|
||||
|
||||
NET_DBG("Neighbor lookup %p", nbr);
|
||||
|
||||
if (!nbr) {
|
||||
nbr = nbr_add(buf, &NET_IPV6_BUF(buf)->src, &lladdr,
|
||||
false, NET_NBR_STALE);
|
||||
|
||||
NET_ASSERT_INFO(nbr, "Could not add neighbor %s [%s]",
|
||||
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src),
|
||||
net_sprint_ll_addr(lladdr.addr, lladdr.len));
|
||||
return;
|
||||
}
|
||||
|
||||
if (net_nbr_link(nbr, net_nbuf_iface(buf), &lladdr) == -EALREADY) {
|
||||
/* Update the lladdr if the node was already known */
|
||||
struct net_linkaddr_storage *cached_lladdr;
|
||||
|
||||
cached_lladdr = net_nbr_get_lladdr(nbr->idx);
|
||||
|
||||
if (memcmp(cached_lladdr->addr, lladdr.addr, lladdr.len)) {
|
||||
|
||||
dbg_update_neighbor_lladdr(&lladdr,
|
||||
cached_lladdr,
|
||||
&NET_IPV6_BUF(buf)->src);
|
||||
|
||||
cached_lladdr->len = lladdr.len;
|
||||
memcpy(cached_lladdr->addr, lladdr.addr, lladdr.len);
|
||||
|
||||
net_nbr_data(nbr)->state = NET_NBR_STALE;
|
||||
} else {
|
||||
if (net_nbr_data(nbr)->state == NET_NBR_INCOMPLETE) {
|
||||
net_nbr_data(nbr)->state = NET_NBR_STALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if NET_DEBUG
|
||||
#define dbg_addr(action, pkt_str, src, dst) \
|
||||
do { \
|
||||
char out[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")]; \
|
||||
\
|
||||
snprintf(out, sizeof(out), net_sprint_ipv6_addr(dst)); \
|
||||
\
|
||||
NET_DBG("%s %s from %s to %s", action, \
|
||||
pkt_str, net_sprint_ipv6_addr(src), out); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define dbg_addr_recv(pkt_str, src, dst) \
|
||||
dbg_addr("Received", pkt_str, src, dst)
|
||||
|
||||
#define dbg_addr_sent(pkt_str, src, dst) \
|
||||
dbg_addr("Sent", pkt_str, src, dst)
|
||||
|
||||
#define dbg_addr_with_tgt(action, pkt_str, src, dst, target) \
|
||||
do { \
|
||||
char out[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")]; \
|
||||
char tgt[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")]; \
|
||||
\
|
||||
snprintf(out, sizeof(out), net_sprint_ipv6_addr(dst)); \
|
||||
snprintf(tgt, sizeof(tgt), net_sprint_ipv6_addr(target)); \
|
||||
\
|
||||
NET_DBG("%s %s from %s to %s, target %s", action, \
|
||||
pkt_str, net_sprint_ipv6_addr(src), out, tgt); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define dbg_addr_recv_tgt(pkt_str, src, dst, tgt) \
|
||||
dbg_addr_with_tgt("Received", pkt_str, src, dst, tgt)
|
||||
|
||||
#define dbg_addr_sent_tgt(pkt_str, src, dst, tgt) \
|
||||
dbg_addr_with_tgt("Sent", pkt_str, src, dst, tgt)
|
||||
#else
|
||||
#define dbg_addr(...)
|
||||
#define dbg_addr_recv(...)
|
||||
#define dbg_addr_sent(...)
|
||||
|
||||
#define dbg_addr_with_tgt(...)
|
||||
#define dbg_addr_recv_tgt(...)
|
||||
#define dbg_addr_sent_tgt(...)
|
||||
#endif
|
||||
|
||||
static enum net_verdict handle_ns_input(struct net_buf *buf)
|
||||
{
|
||||
uint16_t total_len = net_buf_frags_len(buf);
|
||||
struct net_icmpv6_nd_opt_hdr *hdr;
|
||||
struct net_if_addr *ifaddr;
|
||||
uint8_t flags = 0, llao_len;
|
||||
|
||||
dbg_addr_recv_tgt("Neighbor Solicitation",
|
||||
&NET_IPV6_BUF(buf)->src,
|
||||
&NET_IPV6_BUF(buf)->dst,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
NET_STATS(++net_stats.ipv6_nd.recv);
|
||||
|
||||
if ((total_len < (sizeof(struct net_ipv6_hdr) +
|
||||
sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_ns_hdr) +
|
||||
sizeof(struct net_icmpv6_nd_opt_hdr))) ||
|
||||
(NET_ICMP_BUF(buf)->code != 0) ||
|
||||
(NET_IPV6_BUF(buf)->hop_limit != NET_IPV6_ND_HOP_LIMIT) ||
|
||||
net_is_ipv6_addr_mcast(&NET_ICMPV6_NS_BUF(buf)->tgt)) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
net_nbuf_ext_opt_len(buf) = sizeof(struct net_icmpv6_ns_hdr);
|
||||
hdr = NET_ICMPV6_ND_OPT_HDR_BUF(buf);
|
||||
|
||||
/* The parsing gets tricky if the ND struct is split
|
||||
* between two fragments. FIXME later.
|
||||
*/
|
||||
if (buf->frags->len < ((uint8_t *)hdr - buf->frags->data)) {
|
||||
NET_DBG("NS struct split between fragments");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
while (net_nbuf_ext_opt_len(buf) < buf->frags->len) {
|
||||
|
||||
hdr = NET_ICMPV6_ND_OPT_HDR_BUF(buf);
|
||||
|
||||
if (!hdr->len) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (hdr->type) {
|
||||
case NET_ICMPV6_ND_OPT_SLLAO:
|
||||
if (net_is_ipv6_addr_unspecified(
|
||||
&NET_IPV6_BUF(buf)->src)) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
handle_ns_neighbor(buf, hdr);
|
||||
break;
|
||||
|
||||
default:
|
||||
NET_DBG("Unknown ND option 0x%x", hdr->type);
|
||||
break;
|
||||
}
|
||||
|
||||
net_nbuf_ext_opt_len(buf) += hdr->len << 3;
|
||||
}
|
||||
|
||||
ifaddr = net_if_ipv6_addr_lookup_by_iface(net_nbuf_iface(buf),
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
if (!ifaddr) {
|
||||
NET_DBG("No such interface address %s",
|
||||
net_sprint_ipv6_addr(&NET_ICMPV6_NS_BUF(buf)->tgt));
|
||||
goto drop;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
if (net_is_ipv6_addr_unspecified(&NET_IPV6_BUF(buf)->src)) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
#else /* CONFIG_NET_IPV6_NO_DAD */
|
||||
|
||||
/* Do DAD */
|
||||
if (net_is_ipv6_addr_unspecified(&NET_IPV6_BUF(buf)->src)) {
|
||||
|
||||
if (!net_is_ipv6_addr_solicited_node(&NET_IPV6_BUF(buf)->dst)) {
|
||||
NET_DBG("Not solicited node addr %s",
|
||||
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->dst));
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (ifaddr->addr_state == NET_ADDR_TENTATIVE) {
|
||||
NET_DBG("DAD failed for %s iface %p",
|
||||
net_sprint_ipv6_addr(&ifaddr->address.in6_addr));
|
||||
dad_failed(net_nbuf_iface(buf),
|
||||
&ifaddr->address.in6_addr);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* We reuse the received buffer to send the NA */
|
||||
net_ipv6_addr_create_ll_allnodes_mcast(&NET_IPV6_BUF(buf)->dst);
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
||||
net_if_ipv6_select_src_addr(net_nbuf_iface(buf),
|
||||
&NET_IPV6_BUF(buf)->dst));
|
||||
flags = NET_ICMPV6_NA_FLAG_OVERRIDE;
|
||||
goto send_na;
|
||||
}
|
||||
#endif /* CONFIG_NET_IPV6_NO_DAD */
|
||||
|
||||
if (net_is_my_ipv6_addr(&NET_IPV6_BUF(buf)->src)) {
|
||||
NET_DBG("Duplicate IPv6 %s address",
|
||||
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src));
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Address resolution */
|
||||
if (net_is_ipv6_addr_solicited_node(&NET_IPV6_BUF(buf)->dst)) {
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst,
|
||||
&NET_IPV6_BUF(buf)->src);
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
flags = NET_ICMPV6_NA_FLAG_SOLICITED |
|
||||
NET_ICMPV6_NA_FLAG_OVERRIDE;
|
||||
goto send_na;
|
||||
}
|
||||
|
||||
/* Neighbor Unreachability Detection (NUD) */
|
||||
if (net_if_ipv6_addr_lookup_by_iface(net_nbuf_iface(buf),
|
||||
&NET_IPV6_BUF(buf)->dst)) {
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst,
|
||||
&NET_IPV6_BUF(buf)->src);
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
flags = NET_ICMPV6_NA_FLAG_SOLICITED |
|
||||
NET_ICMPV6_NA_FLAG_OVERRIDE;
|
||||
goto send_na;
|
||||
} else {
|
||||
NET_DBG("NUD failed");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
send_na:
|
||||
llao_len = get_llao_len(net_nbuf_iface(buf));
|
||||
|
||||
net_nbuf_ll_swap(buf);
|
||||
net_nbuf_ext_len(buf) = 0;
|
||||
|
||||
setup_headers(buf, sizeof(struct net_icmpv6_na_hdr) + llao_len,
|
||||
NET_ICMPV6_NA);
|
||||
|
||||
net_ipaddr_copy(&NET_ICMPV6_NA_BUF(buf)->tgt,
|
||||
&ifaddr->address.in6_addr);
|
||||
|
||||
set_llao(&net_nbuf_iface(buf)->link_addr,
|
||||
net_nbuf_icmp_data(buf) + sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_na_hdr),
|
||||
llao_len, NET_ICMPV6_ND_OPT_TLLAO);
|
||||
|
||||
NET_ICMPV6_NA_BUF(buf)->flags = flags;
|
||||
|
||||
NET_ICMP_BUF(buf)->chksum = 0;
|
||||
NET_ICMP_BUF(buf)->chksum = ~net_calc_chksum_icmpv6(buf);
|
||||
|
||||
net_nbuf_len(buf->frags) = NET_IPV6ICMPH_LEN +
|
||||
sizeof(struct net_icmpv6_na_hdr) +
|
||||
llao_len;
|
||||
|
||||
if (net_send_data(buf) < 0) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
NET_STATS(++net_stats.ipv6_nd.sent);
|
||||
|
||||
return NET_OK;
|
||||
|
||||
drop:
|
||||
NET_STATS(++net_stats.ipv6_nd.drop);
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
static inline bool handle_na_neighbor(struct net_buf *buf,
|
||||
struct net_icmpv6_nd_opt_hdr *hdr,
|
||||
uint8_t *tllao)
|
||||
{
|
||||
bool lladdr_changed = false;
|
||||
struct net_nbr *nbr;
|
||||
struct net_linkaddr_storage *cached_lladdr;
|
||||
struct net_buf *pending;
|
||||
|
||||
nbr = nbr_lookup(&net_neighbor.table, net_nbuf_iface(buf),
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
NET_DBG("Neighbor lookup %p", nbr);
|
||||
|
||||
if (!nbr || nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
|
||||
NET_DBG("No such neighbor found, msg discarded");
|
||||
return false;
|
||||
}
|
||||
|
||||
cached_lladdr = net_nbr_get_lladdr(nbr->idx);
|
||||
if (!cached_lladdr) {
|
||||
NET_DBG("No lladdr but index defined");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tllao) {
|
||||
lladdr_changed = memcmp(&tllao[NET_ICMPV6_OPT_DATA_OFFSET],
|
||||
cached_lladdr->addr,
|
||||
cached_lladdr->len);
|
||||
}
|
||||
|
||||
/* Update the cached address if we do not yet known it */
|
||||
if (net_nbr_data(nbr)->state == NET_NBR_INCOMPLETE) {
|
||||
if (!tllao) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lladdr_changed) {
|
||||
dbg_update_neighbor_lladdr_raw(
|
||||
&tllao[NET_ICMPV6_OPT_DATA_OFFSET],
|
||||
cached_lladdr,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
memcpy(cached_lladdr->addr,
|
||||
&tllao[NET_ICMPV6_OPT_DATA_OFFSET],
|
||||
cached_lladdr->len);
|
||||
}
|
||||
|
||||
if (net_is_solicited(buf)) {
|
||||
net_nbr_data(nbr)->state = NET_NBR_REACHABLE;
|
||||
net_nbr_data(nbr)->ns_count = 0;
|
||||
|
||||
/* FIXME - reachable timer here */
|
||||
} else {
|
||||
net_nbr_data(nbr)->state = NET_NBR_STALE;
|
||||
}
|
||||
|
||||
net_nbr_data(buf)->is_router = net_is_router(buf);
|
||||
|
||||
goto send_pending;
|
||||
}
|
||||
|
||||
/* We do not update the address if override bit is not set
|
||||
* and we have a valid address in the cache.
|
||||
*/
|
||||
if (!net_is_override(buf) && lladdr_changed) {
|
||||
if (net_nbr_data(nbr)->state == NET_NBR_REACHABLE) {
|
||||
net_nbr_data(nbr)->state = NET_NBR_STALE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (net_is_override(buf) ||
|
||||
(!net_is_override(buf) && tllao && !lladdr_changed)) {
|
||||
|
||||
if (lladdr_changed) {
|
||||
dbg_update_neighbor_lladdr_raw(
|
||||
&tllao[NET_ICMPV6_OPT_DATA_OFFSET],
|
||||
cached_lladdr,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
memcpy(cached_lladdr->addr,
|
||||
&tllao[NET_ICMPV6_OPT_DATA_OFFSET],
|
||||
cached_lladdr->len);
|
||||
}
|
||||
|
||||
if (net_is_solicited(buf)) {
|
||||
net_nbr_data(nbr)->state = NET_NBR_REACHABLE;
|
||||
|
||||
/* FIXME - rechable timer here */
|
||||
} else {
|
||||
if (lladdr_changed) {
|
||||
net_nbr_data(nbr)->state = NET_NBR_STALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (net_nbr_data(nbr)->is_router && !net_is_router(buf)) {
|
||||
/* Update the routing if the peer is no longer
|
||||
* a router.
|
||||
*/
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
net_nbr_data(nbr)->is_router = net_is_router(buf);
|
||||
|
||||
send_pending:
|
||||
/* Next send any pending messages to the peer. */
|
||||
pending = net_nbr_data(nbr)->pending;
|
||||
|
||||
NET_DBG("Sending pending to %s lladdr %s",
|
||||
net_sprint_ipv6_addr(&NET_IPV6_BUF(pending)->dst),
|
||||
net_sprint_ll_addr(cached_lladdr->addr,
|
||||
cached_lladdr->len));
|
||||
|
||||
if (net_send_data(pending) < 0) {
|
||||
net_nbuf_unref(pending);
|
||||
net_nbr_data(nbr)->pending = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum net_verdict handle_na_input(struct net_buf *buf)
|
||||
{
|
||||
uint16_t total_len = net_buf_frags_len(buf);
|
||||
struct net_icmpv6_nd_opt_hdr *hdr;
|
||||
struct net_if_addr *ifaddr;
|
||||
uint8_t *tllao = NULL;
|
||||
|
||||
dbg_addr_recv_tgt("Neighbor Advertisement",
|
||||
&NET_IPV6_BUF(buf)->src,
|
||||
&NET_IPV6_BUF(buf)->dst,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
NET_STATS(++net_stats.ipv6_nd.recv);
|
||||
|
||||
if ((total_len < (sizeof(struct net_ipv6_hdr) +
|
||||
sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_na_hdr) +
|
||||
sizeof(struct net_icmpv6_nd_opt_hdr))) ||
|
||||
(NET_ICMP_BUF(buf)->code != 0) ||
|
||||
(NET_IPV6_BUF(buf)->hop_limit != NET_IPV6_ND_HOP_LIMIT) ||
|
||||
net_is_ipv6_addr_mcast(&NET_ICMPV6_NS_BUF(buf)->tgt) ||
|
||||
(net_is_solicited(buf) &&
|
||||
net_is_ipv6_addr_mcast(&NET_IPV6_BUF(buf)->dst))) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
net_nbuf_ext_opt_len(buf) = sizeof(struct net_icmpv6_na_hdr);
|
||||
hdr = NET_ICMPV6_ND_OPT_HDR_BUF(buf);
|
||||
|
||||
/* The parsing gets tricky if the ND struct is split
|
||||
* between two fragments. FIXME later.
|
||||
*/
|
||||
if (buf->frags->len < ((uint8_t *)hdr - buf->frags->data)) {
|
||||
NET_DBG("NA struct split between fragments");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
while (net_nbuf_ext_opt_len(buf) < buf->frags->len) {
|
||||
|
||||
hdr = NET_ICMPV6_ND_OPT_HDR_BUF(buf);
|
||||
|
||||
if (!hdr->len) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (hdr->type) {
|
||||
case NET_ICMPV6_ND_OPT_TLLAO:
|
||||
tllao = (uint8_t *)hdr;
|
||||
break;
|
||||
|
||||
default:
|
||||
NET_DBG("Unknown ND option 0x%x", hdr->type);
|
||||
break;
|
||||
}
|
||||
|
||||
net_nbuf_ext_opt_len(buf) += hdr->len << 3;
|
||||
}
|
||||
|
||||
ifaddr = net_if_ipv6_addr_lookup_by_iface(net_nbuf_iface(buf),
|
||||
&NET_ICMPV6_NA_BUF(buf)->tgt);
|
||||
if (ifaddr) {
|
||||
NET_DBG("Interface %p already has address %s",
|
||||
net_nbuf_iface(buf),
|
||||
net_sprint_ipv6_addr(&NET_ICMPV6_NA_BUF(buf)->tgt));
|
||||
|
||||
#if !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
if (ifaddr->addr_state == NET_ADDR_TENTATIVE) {
|
||||
dad_failed(net_nbuf_iface(buf),
|
||||
&NET_ICMPV6_NA_BUF(buf)->tgt);
|
||||
}
|
||||
#endif /* !CONFIG_NET_IPV6_NO_DAD */
|
||||
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!handle_na_neighbor(buf, hdr, tllao)) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
NET_STATS(++net_stats.ipv6_nd.sent);
|
||||
return NET_OK;
|
||||
|
||||
drop:
|
||||
NET_STATS(++net_stats.ipv6_nd.drop);
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
int net_ipv6_send_ns(struct net_if *iface,
|
||||
struct net_buf *pending,
|
||||
struct in6_addr *src,
|
||||
struct in6_addr *dst,
|
||||
struct in6_addr *tgt,
|
||||
bool is_my_address)
|
||||
{
|
||||
struct net_buf *buf, *frag;
|
||||
uint8_t llao_len;
|
||||
|
||||
buf = net_nbuf_get_reserve_tx(0);
|
||||
|
||||
NET_ASSERT_INFO(buf, "Out of TX buffers");
|
||||
|
||||
if (pending) {
|
||||
frag = net_nbuf_get_reserve_data(net_nbuf_ll_reserve(pending));
|
||||
} else {
|
||||
frag = net_nbuf_get_reserve_data(net_if_get_ll_reserve(iface));
|
||||
}
|
||||
|
||||
NET_ASSERT_INFO(frag, "Out of DATA buffers");
|
||||
|
||||
net_buf_frag_add(buf, frag);
|
||||
|
||||
net_nbuf_ll_reserve(buf) = net_buf_headroom(frag);
|
||||
net_nbuf_iface(buf) = iface;
|
||||
net_nbuf_family(buf) = AF_INET6;
|
||||
net_nbuf_ip_hdr_len(buf) = sizeof(struct net_ipv6_hdr);
|
||||
|
||||
net_nbuf_ll_clear(buf);
|
||||
|
||||
llao_len = get_llao_len(net_nbuf_iface(buf));
|
||||
|
||||
setup_headers(buf, sizeof(struct net_icmpv6_ns_hdr) + llao_len,
|
||||
NET_ICMPV6_NS);
|
||||
|
||||
if (!dst) {
|
||||
net_ipv6_addr_create_solicited_node(tgt,
|
||||
&NET_IPV6_BUF(buf)->dst);
|
||||
} else {
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst, dst);
|
||||
}
|
||||
|
||||
NET_ICMPV6_NS_BUF(buf)->reserved = 0;
|
||||
|
||||
net_ipaddr_copy(&NET_ICMPV6_NS_BUF(buf)->tgt, tgt);
|
||||
|
||||
if (is_my_address) {
|
||||
/* DAD */
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
||||
net_if_ipv6_unspecified_addr());
|
||||
|
||||
NET_IPV6_BUF(buf)->len[1] -= llao_len;
|
||||
|
||||
net_buf_add(frag,
|
||||
sizeof(struct net_ipv6_hdr) +
|
||||
sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_ns_hdr));
|
||||
} else {
|
||||
if (src) {
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src, src);
|
||||
} else {
|
||||
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
||||
net_if_ipv6_select_src_addr(
|
||||
net_nbuf_iface(buf),
|
||||
&NET_IPV6_BUF(buf)->dst));
|
||||
}
|
||||
|
||||
if (net_is_ipv6_addr_unspecified(&NET_IPV6_BUF(buf)->src)) {
|
||||
NET_DBG("No source address for NS");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
set_llao(&net_nbuf_iface(buf)->link_addr,
|
||||
net_nbuf_icmp_data(buf) +
|
||||
sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_ns_hdr),
|
||||
llao_len, NET_ICMPV6_ND_OPT_SLLAO);
|
||||
|
||||
net_buf_add(frag,
|
||||
sizeof(struct net_ipv6_hdr) +
|
||||
sizeof(struct net_icmp_hdr) +
|
||||
sizeof(struct net_icmpv6_ns_hdr) +
|
||||
sizeof(struct net_icmpv6_nd_opt_hdr) + llao_len);
|
||||
}
|
||||
|
||||
NET_ICMP_BUF(buf)->chksum = 0;
|
||||
NET_ICMP_BUF(buf)->chksum = ~net_calc_chksum_icmpv6(buf);
|
||||
|
||||
if (pending) {
|
||||
struct net_nbr *nbr;
|
||||
|
||||
nbr = nbr_new(&NET_ICMPV6_NS_BUF(buf)->tgt,
|
||||
NET_NBR_INCOMPLETE);
|
||||
if (!nbr) {
|
||||
NET_DBG("Could not create new neighbor %s",
|
||||
net_sprint_ipv6_addr(
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt));
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!net_nbr_data(nbr)->pending) {
|
||||
net_nbr_data(nbr)->pending = pending;
|
||||
} else {
|
||||
NET_DBG("Buffer %p already pending for operation. "
|
||||
"Discarding this buf %p",
|
||||
net_nbr_data(nbr)->pending, buf);
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
|
||||
dbg_addr_sent_tgt("Neighbor Solicitation",
|
||||
&NET_IPV6_BUF(buf)->src,
|
||||
&NET_IPV6_BUF(buf)->dst,
|
||||
&NET_ICMPV6_NS_BUF(buf)->tgt);
|
||||
|
||||
if (net_send_data(buf) < 0) {
|
||||
goto drop;
|
||||
}
|
||||
|
||||
NET_STATS(++net_stats.ipv6_nd.sent);
|
||||
|
||||
return 0;
|
||||
|
||||
drop:
|
||||
net_nbuf_unref(buf);
|
||||
NET_STATS(++net_stats.ipv6_nd.drop);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct net_icmpv6_handler ns_input_handler = {
|
||||
.type = NET_ICMPV6_NS,
|
||||
.code = 0,
|
||||
.handler = handle_ns_input,
|
||||
};
|
||||
|
||||
static struct net_icmpv6_handler na_input_handler = {
|
||||
.type = NET_ICMPV6_NA,
|
||||
.code = 0,
|
||||
.handler = handle_na_input,
|
||||
};
|
||||
|
||||
void net_ipv6_init(void)
|
||||
{
|
||||
net_icmpv6_register_handler(&ns_input_handler);
|
||||
net_icmpv6_register_handler(&na_input_handler);
|
||||
}
|
50
net/yaip/ipv6.h
Normal file
50
net/yaip/ipv6.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/** @file
|
||||
@brief IPv6 data handler
|
||||
|
||||
This is not to be included by the application.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __IPV6_H
|
||||
#define __IPV6_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <net/net_ip.h>
|
||||
#include <net/nbuf.h>
|
||||
#include <net/net_if.h>
|
||||
|
||||
#include "icmpv6.h"
|
||||
|
||||
#define NET_IPV6_ND_HOP_LIMIT 255
|
||||
|
||||
#if !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
int net_ipv6_start_dad(struct net_if *iface, struct net_if_addr *ifaddr);
|
||||
#endif
|
||||
|
||||
int net_ipv6_send_ns(struct net_if *iface, struct net_buf *pending,
|
||||
struct in6_addr *src, struct in6_addr *dst,
|
||||
struct in6_addr *tgt, bool is_my_address);
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
void net_ipv6_init(void);
|
||||
#else
|
||||
#define net_ipv6_init(...)
|
||||
#endif
|
||||
|
||||
#endif /* __IPV6_H */
|
|
@ -411,6 +411,7 @@ int net_recv_data(struct net_if *iface, struct net_buf *buf)
|
|||
static inline void l3_init(void)
|
||||
{
|
||||
net_icmpv6_init();
|
||||
net_ipv6_init();
|
||||
|
||||
NET_DBG("Network L3 init done");
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <net/arp.h>
|
||||
|
||||
#include "net_private.h"
|
||||
#include "ipv6.h"
|
||||
|
||||
/* net_if dedicated section limiters */
|
||||
extern struct net_if __net_if_start[];
|
||||
|
@ -102,6 +103,51 @@ struct net_if *net_if_get_by_link_addr(struct net_linkaddr *ll_addr)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV6_NO_DAD)
|
||||
#define DAD_TIMEOUT (sys_clock_ticks_per_sec / 10)
|
||||
|
||||
static void dad_timeout(struct nano_work *work)
|
||||
{
|
||||
/* This means that the DAD succeed. */
|
||||
struct net_if_addr *ifaddr = CONTAINER_OF(work,
|
||||
struct net_if_addr,
|
||||
dad_timer);
|
||||
|
||||
NET_DBG("DAD succeeded for %s",
|
||||
net_sprint_ipv6_addr(&ifaddr->address.in6_addr));
|
||||
|
||||
ifaddr->addr_state = NET_ADDR_PREFERRED;
|
||||
}
|
||||
|
||||
void net_if_start_dad(struct net_if *iface)
|
||||
{
|
||||
struct net_if_addr *ifaddr;
|
||||
struct in6_addr addr = { 0 };
|
||||
|
||||
net_ipv6_addr_create_iid(&addr, &iface->link_addr);
|
||||
|
||||
ifaddr = net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, 0);
|
||||
if (!ifaddr) {
|
||||
NET_ERR("Cannot add %s address to interface %p, DAD fails",
|
||||
net_sprint_ipv6_addr(&addr), iface);
|
||||
return;
|
||||
}
|
||||
|
||||
ifaddr->addr_state = NET_ADDR_TENTATIVE;
|
||||
ifaddr->dad_count = 1;
|
||||
|
||||
NET_DBG("Interface %p ll addr %s tentative IPv6 addr %s", iface,
|
||||
net_sprint_ll_addr(iface->link_addr.addr,
|
||||
iface->link_addr.len),
|
||||
net_sprint_ipv6_addr(&ifaddr->address.in6_addr));
|
||||
|
||||
if (!net_ipv6_start_dad(iface, ifaddr)) {
|
||||
nano_delayed_work_init(&ifaddr->dad_timer, dad_timeout);
|
||||
nano_delayed_work_submit(&ifaddr->dad_timer, DAD_TIMEOUT);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct net_if_addr *net_if_ipv6_addr_lookup(struct in6_addr *addr)
|
||||
{
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
extern void net_nbuf_init(void);
|
||||
extern void net_if_init(void);
|
||||
extern void net_context_init(void);
|
||||
extern void net_ipv6_init(void);
|
||||
|
||||
extern char *net_byte_to_hex(uint8_t *ptr, uint8_t byte, char base, bool pad);
|
||||
extern char *net_sprint_ll_addr_buf(const uint8_t *ll, uint8_t ll_len,
|
||||
|
|
Loading…
Reference in a new issue