net: ipv6: Rework net_ipv6_nbr_add

This makes net_ipv6_nbr_add able to update entries so it can be reused
when receiving RA or NS requests, so it now performs a lookup before
creating a new entry (using nbr_new to reuse more code) and in case it
finds a match attempts to update the lladdr.

Change-Id: I305a67a955e037cbbb862fef947a5fcfe131507c
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2017-04-12 13:38:07 +03:00 committed by Jukka Rissanen
commit 076ddacca5
2 changed files with 139 additions and 210 deletions

View file

@ -300,11 +300,9 @@ static void nbr_init(struct net_nbr *nbr, struct net_if *iface,
ns_reply_timeout);
}
struct net_nbr *net_ipv6_nbr_add(struct net_if *iface,
struct in6_addr *addr,
struct net_linkaddr *lladdr,
bool is_router,
enum net_ipv6_nbr_state state)
static struct net_nbr *nbr_new(struct net_if *iface,
struct in6_addr *addr, bool is_router,
enum net_ipv6_nbr_state state)
{
struct net_nbr *nbr = net_nbr_get(&net_neighbor.table);
@ -312,11 +310,135 @@ struct net_nbr *net_ipv6_nbr_add(struct net_if *iface,
return NULL;
}
nbr_init(nbr, iface, addr, is_router, state);
nbr_init(nbr, iface, addr, true, state);
if (net_nbr_link(nbr, iface, lladdr)) {
nbr_free(nbr);
return NULL;
NET_DBG("nbr %p iface %p state %d IPv6 %s",
nbr, iface, state, net_sprint_ipv6_addr(addr));
return nbr;
}
#if defined(CONFIG_NET_DEBUG_IPV6)
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")];
snprintk(out, sizeof(out), "%s",
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);
}
#define dbg_addr(action, pkt_str, src, dst) \
do { \
char out[NET_IPV6_ADDR_LEN]; \
\
snprintk(out, sizeof(out), "%s", \
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[NET_IPV6_ADDR_LEN]; \
char tgt[NET_IPV6_ADDR_LEN]; \
\
snprintk(out, sizeof(out), "%s", \
net_sprint_ipv6_addr(dst)); \
snprintk(tgt, sizeof(tgt), "%s", \
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_update_neighbor_lladdr(...)
#define dbg_update_neighbor_lladdr_raw(...)
#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 /* CONFIG_NET_DEBUG_IPV6 */
struct net_nbr *net_ipv6_nbr_add(struct net_if *iface,
struct in6_addr *addr,
struct net_linkaddr *lladdr,
bool is_router,
enum net_ipv6_nbr_state state)
{
struct net_nbr *nbr;
nbr = nbr_lookup(&net_neighbor.table, iface, addr);
if (!nbr) {
nbr = nbr_new(iface, addr, is_router, state);
if (!nbr) {
NET_ERR("Could not add router neighbor %s [%s]",
net_sprint_ipv6_addr(addr),
net_sprint_ll_addr(lladdr->addr, lladdr->len));
return NULL;
}
}
if (net_nbr_link(nbr, iface, 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, addr);
net_linkaddr_set(cached_lladdr, lladdr->addr,
lladdr->len);
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
} else if (net_ipv6_nbr_data(nbr)->state ==
NET_IPV6_NBR_STATE_INCOMPLETE) {
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
}
}
if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_INCOMPLETE) {
/* Send NS so that we can verify that the neighbor is
* reachable.
*/
net_ipv6_send_ns(iface, NULL, NULL, NULL, addr, false);
}
NET_DBG("[%d] nbr %p state %d router %d IPv6 %s ll %s",
@ -328,31 +450,12 @@ struct net_nbr *net_ipv6_nbr_add(struct net_if *iface,
}
static inline struct net_nbr *nbr_add(struct net_buf *buf,
struct in6_addr *addr,
struct net_linkaddr *lladdr,
bool is_router,
enum net_ipv6_nbr_state state)
{
return net_ipv6_nbr_add(net_nbuf_iface(buf), addr, lladdr,
is_router, state);
}
static struct net_nbr *nbr_new(struct net_if *iface,
struct in6_addr *addr,
enum net_ipv6_nbr_state state)
{
struct net_nbr *nbr = net_nbr_get(&net_neighbor.table);
if (!nbr) {
return NULL;
}
nbr_init(nbr, iface, addr, false, state);
NET_DBG("nbr %p iface %p state %d IPv6 %s",
nbr, iface, state, net_sprint_ipv6_addr(addr));
return nbr;
return net_ipv6_nbr_add(net_nbuf_iface(buf), &NET_IPV6_BUF(buf)->src,
lladdr, is_router, state);
}
void net_neighbor_data_remove(struct net_nbr *nbr)
@ -601,84 +704,6 @@ static inline bool dad_failed(struct net_if *iface, struct in6_addr *addr)
}
#endif /* CONFIG_NET_IPV6_DAD */
#if defined(CONFIG_NET_DEBUG_IPV6)
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")];
snprintk(out, sizeof(out), "%s",
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);
}
#define dbg_addr(action, pkt_str, src, dst) \
do { \
char out[NET_IPV6_ADDR_LEN]; \
\
snprintk(out, sizeof(out), "%s", \
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[NET_IPV6_ADDR_LEN]; \
char tgt[NET_IPV6_ADDR_LEN]; \
\
snprintk(out, sizeof(out), "%s", \
net_sprint_ipv6_addr(dst)); \
snprintk(tgt, sizeof(tgt), "%s", \
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_update_neighbor_lladdr(...)
#define dbg_update_neighbor_lladdr_raw(...)
#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 /* CONFIG_NET_DEBUG_IPV6 */
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
/* If the reserve has changed, we need to adjust it accordingly in the
@ -1014,7 +1039,6 @@ static void setup_headers(struct net_buf *buf, uint8_t nd6_len,
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,
@ -1029,60 +1053,7 @@ static inline void handle_ns_neighbor(struct net_buf *buf,
lladdr.len = net_nbuf_ll_src(buf)->len;
}
nbr = nbr_lookup(&net_neighbor.table, net_nbuf_iface(buf),
&NET_IPV6_BUF(buf)->src);
NET_DBG("Neighbor lookup %p iface %p addr %s", nbr,
net_nbuf_iface(buf),
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src));
if (!nbr) {
nbr_print();
nbr = nbr_new(net_nbuf_iface(buf),
&NET_IPV6_BUF(buf)->src,
NET_IPV6_NBR_STATE_INCOMPLETE);
if (nbr) {
NET_DBG("Added %s to nbr cache",
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src));
} else {
NET_ERR("Could not add neighbor %s",
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src));
return;
}
/* Send NS so that we can verify that the neighbor is
* reachable.
*/
net_ipv6_send_ns(net_nbuf_iface(buf), NULL, NULL, NULL,
&net_ipv6_nbr_data(nbr)->addr, false);
}
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);
net_linkaddr_set(cached_lladdr, lladdr.addr,
lladdr.len);
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
} else {
if (net_ipv6_nbr_data(nbr)->state ==
NET_IPV6_NBR_STATE_INCOMPLETE) {
ipv6_nbr_set_state(nbr,
NET_IPV6_NBR_STATE_STALE);
}
}
}
nbr_add(buf, &lladdr, false, NET_IPV6_NBR_STATE_INCOMPLETE);
}
int net_ipv6_send_na(struct net_if *iface, struct in6_addr *src,
@ -1794,7 +1765,7 @@ int net_ipv6_send_ns(struct net_if *iface,
nbr_print();
nbr = nbr_new(net_nbuf_iface(buf),
&NET_ICMPV6_NS_BUF(buf)->tgt,
&NET_ICMPV6_NS_BUF(buf)->tgt, false,
NET_IPV6_NBR_STATE_INCOMPLETE);
if (!nbr) {
NET_DBG("Could not create new neighbor %s",
@ -1961,52 +1932,7 @@ static inline struct net_buf *handle_ra_neighbor(struct net_buf *buf,
}
}
*nbr = nbr_lookup(&net_neighbor.table, net_nbuf_iface(buf),
&NET_IPV6_BUF(buf)->src);
NET_DBG("Neighbor lookup %p iface %p addr %s", *nbr,
net_nbuf_iface(buf),
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src));
if (!*nbr) {
nbr_print();
*nbr = nbr_add(buf, &NET_IPV6_BUF(buf)->src, &lladdr,
true, NET_IPV6_NBR_STATE_STALE);
if (!*nbr) {
NET_ERR("Could not add router neighbor %s [%s]",
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src),
net_sprint_ll_addr(lladdr.addr, lladdr.len));
return NULL;
}
}
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);
net_linkaddr_set(cached_lladdr, lladdr.addr,
lladdr.len);
ipv6_nbr_set_state(*nbr, NET_IPV6_NBR_STATE_STALE);
} else {
if (net_ipv6_nbr_data(*nbr)->state ==
NET_IPV6_NBR_STATE_INCOMPLETE) {
ipv6_nbr_set_state(*nbr,
NET_IPV6_NBR_STATE_STALE);
}
}
}
net_ipv6_nbr_data(*nbr)->is_router = true;
*nbr = nbr_add(buf, &lladdr, true, NET_IPV6_NBR_STATE_STALE);
return frag;
}

View file

@ -276,6 +276,9 @@ struct in6_addr *net_ipv6_nbr_lookup_by_index(struct net_if *iface,
/**
* @brief Add a neighbour to neighbor cache
*
* Add a neighbour to the cache after performing a lookup and in case
* there exists an entry in the cache update its state and lladdr.
*
* @param iface A valid pointer on a network interface
* @param addr Neighbor IPv6 address
* @param lladdr Neighbor link layer address