zephyr/subsys/net/l2/ethernet/arp.c
Jukka Rissanen 3a67c6d8f5 net: arp: Enhance debug prints by printing interface index
Print also network interface index together with the pointer value
in order to get more useful information what is going on in the system.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
2024-03-28 09:41:38 +00:00

841 lines
21 KiB
C

/** @file
* @brief ARP related functions
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_arp, CONFIG_NET_ARP_LOG_LEVEL);
#include <errno.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_stats.h>
#include "arp.h"
#include "net_private.h"
#define NET_BUF_TIMEOUT K_MSEC(100)
#define ARP_REQUEST_TIMEOUT (2 * MSEC_PER_SEC)
static bool arp_cache_initialized;
static struct arp_entry arp_entries[CONFIG_NET_ARP_TABLE_SIZE];
static sys_slist_t arp_free_entries;
static sys_slist_t arp_pending_entries;
static sys_slist_t arp_table;
static struct k_work_delayable arp_request_timer;
static struct k_mutex arp_mutex;
static void arp_entry_cleanup(struct arp_entry *entry, bool pending)
{
NET_DBG("entry %p", entry);
if (pending) {
struct net_pkt *pkt;
while (!k_fifo_is_empty(&entry->pending_queue)) {
pkt = k_fifo_get(&entry->pending_queue, K_FOREVER);
NET_DBG("Releasing pending pkt %p (ref %ld)",
pkt,
atomic_get(&pkt->atomic_ref) - 1);
net_pkt_unref(pkt);
}
}
entry->iface = NULL;
(void)memset(&entry->ip, 0, sizeof(struct in_addr));
(void)memset(&entry->eth, 0, sizeof(struct net_eth_addr));
}
static struct arp_entry *arp_entry_find(sys_slist_t *list,
struct net_if *iface,
struct in_addr *dst,
sys_snode_t **previous)
{
struct arp_entry *entry;
SYS_SLIST_FOR_EACH_CONTAINER(list, entry, node) {
NET_DBG("iface %d (%p) dst %s",
net_if_get_by_iface(iface), iface,
net_sprint_ipv4_addr(&entry->ip));
if (entry->iface == iface &&
net_ipv4_addr_cmp(&entry->ip, dst)) {
return entry;
}
if (previous) {
*previous = &entry->node;
}
}
return NULL;
}
static inline struct arp_entry *arp_entry_find_move_first(struct net_if *iface,
struct in_addr *dst)
{
sys_snode_t *prev = NULL;
struct arp_entry *entry;
NET_DBG("dst %s", net_sprint_ipv4_addr(dst));
entry = arp_entry_find(&arp_table, iface, dst, &prev);
if (entry) {
/* Let's assume the target is going to be accessed
* more than once here in a short time frame. So we
* place the entry first in position into the table
* in order to reduce subsequent find.
*/
if (&entry->node != sys_slist_peek_head(&arp_table)) {
sys_slist_remove(&arp_table, prev, &entry->node);
sys_slist_prepend(&arp_table, &entry->node);
}
}
return entry;
}
static inline
struct arp_entry *arp_entry_find_pending(struct net_if *iface,
struct in_addr *dst)
{
NET_DBG("dst %s", net_sprint_ipv4_addr(dst));
return arp_entry_find(&arp_pending_entries, iface, dst, NULL);
}
static struct arp_entry *arp_entry_get_pending(struct net_if *iface,
struct in_addr *dst)
{
sys_snode_t *prev = NULL;
struct arp_entry *entry;
NET_DBG("dst %s", net_sprint_ipv4_addr(dst));
entry = arp_entry_find(&arp_pending_entries, iface, dst, &prev);
if (entry) {
/* We remove the entry from the pending list */
sys_slist_remove(&arp_pending_entries, prev, &entry->node);
}
if (sys_slist_is_empty(&arp_pending_entries)) {
k_work_cancel_delayable(&arp_request_timer);
}
return entry;
}
static struct arp_entry *arp_entry_get_free(void)
{
sys_snode_t *node;
node = sys_slist_peek_head(&arp_free_entries);
if (!node) {
return NULL;
}
/* We remove the node from the free list */
sys_slist_remove(&arp_free_entries, NULL, node);
return CONTAINER_OF(node, struct arp_entry, node);
}
static struct arp_entry *arp_entry_get_last_from_table(void)
{
sys_snode_t *node;
/* We assume last entry is the oldest one,
* so is the preferred one to be taken out.
*/
node = sys_slist_peek_tail(&arp_table);
if (!node) {
return NULL;
}
sys_slist_find_and_remove(&arp_table, node);
return CONTAINER_OF(node, struct arp_entry, node);
}
static void arp_entry_register_pending(struct arp_entry *entry)
{
NET_DBG("dst %s", net_sprint_ipv4_addr(&entry->ip));
sys_slist_append(&arp_pending_entries, &entry->node);
entry->req_start = k_uptime_get_32();
/* Let's start the timer if necessary */
if (!k_work_delayable_remaining_get(&arp_request_timer)) {
k_work_reschedule(&arp_request_timer,
K_MSEC(ARP_REQUEST_TIMEOUT));
}
}
static void arp_request_timeout(struct k_work *work)
{
uint32_t current = k_uptime_get_32();
struct arp_entry *entry, *next;
ARG_UNUSED(work);
k_mutex_lock(&arp_mutex, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&arp_pending_entries,
entry, next, node) {
if ((int32_t)(entry->req_start +
ARP_REQUEST_TIMEOUT - current) > 0) {
break;
}
arp_entry_cleanup(entry, true);
sys_slist_remove(&arp_pending_entries, NULL, &entry->node);
sys_slist_append(&arp_free_entries, &entry->node);
entry = NULL;
}
if (entry) {
k_work_reschedule(&arp_request_timer,
K_MSEC(entry->req_start +
ARP_REQUEST_TIMEOUT - current));
}
k_mutex_unlock(&arp_mutex);
}
static inline struct in_addr *if_get_addr(struct net_if *iface,
struct in_addr *addr)
{
struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
if (!ipv4) {
return NULL;
}
ARRAY_FOR_EACH(ipv4->unicast, i) {
if (ipv4->unicast[i].ipv4.is_used &&
ipv4->unicast[i].ipv4.address.family == AF_INET &&
ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED &&
(!addr ||
net_ipv4_addr_cmp(addr,
&ipv4->unicast[i].ipv4.address.in_addr))) {
return &ipv4->unicast[i].ipv4.address.in_addr;
}
}
return NULL;
}
static inline struct net_pkt *arp_prepare(struct net_if *iface,
struct in_addr *next_addr,
struct arp_entry *entry,
struct net_pkt *pending,
struct in_addr *current_ip)
{
struct net_arp_hdr *hdr;
struct in_addr *my_addr;
struct net_pkt *pkt;
if (current_ip) {
/* This is the IPv4 autoconf case where we have already
* things setup so no need to allocate new net_pkt
*/
pkt = pending;
} else {
pkt = net_pkt_alloc_with_buffer(iface,
sizeof(struct net_arp_hdr),
AF_UNSPEC, 0, NET_BUF_TIMEOUT);
if (!pkt) {
return NULL;
}
/* Avoid recursive loop with network packet capturing */
if (IS_ENABLED(CONFIG_NET_CAPTURE) && pending) {
net_pkt_set_captured(pkt, net_pkt_is_captured(pending));
}
if (IS_ENABLED(CONFIG_NET_VLAN) && pending) {
net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(pending));
}
}
net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr));
hdr = NET_ARP_HDR(pkt);
/* If entry is not set, then we are just about to send
* an ARP request using the data in pending net_pkt.
* This can happen if there is already a pending ARP
* request and we want to send it again.
*/
if (entry) {
if (!net_pkt_ipv4_auto(pkt)) {
k_fifo_put(&entry->pending_queue, net_pkt_ref(pending));
}
entry->iface = net_pkt_iface(pkt);
net_ipaddr_copy(&entry->ip, next_addr);
net_pkt_lladdr_src(pkt)->addr =
(uint8_t *)net_if_get_link_addr(entry->iface)->addr;
arp_entry_register_pending(entry);
} else {
net_pkt_lladdr_src(pkt)->addr =
(uint8_t *)net_if_get_link_addr(iface)->addr;
}
net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr);
net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)net_eth_broadcast_addr();
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
hdr->hwtype = htons(NET_ARP_HTYPE_ETH);
hdr->protocol = htons(NET_ETH_PTYPE_IP);
hdr->hwlen = sizeof(struct net_eth_addr);
hdr->protolen = sizeof(struct in_addr);
hdr->opcode = htons(NET_ARP_REQUEST);
(void)memset(&hdr->dst_hwaddr.addr, 0x00, sizeof(struct net_eth_addr));
net_ipv4_addr_copy_raw(hdr->dst_ipaddr, (uint8_t *)next_addr);
memcpy(hdr->src_hwaddr.addr, net_pkt_lladdr_src(pkt)->addr,
sizeof(struct net_eth_addr));
if (net_pkt_ipv4_auto(pkt)) {
my_addr = current_ip;
} else if (!entry) {
my_addr = (struct in_addr *)NET_IPV4_HDR(pending)->src;
} else {
my_addr = if_get_addr(entry->iface, current_ip);
}
if (my_addr) {
net_ipv4_addr_copy_raw(hdr->src_ipaddr, (uint8_t *)my_addr);
} else {
(void)memset(&hdr->src_ipaddr, 0, sizeof(struct in_addr));
}
return pkt;
}
struct net_pkt *net_arp_prepare(struct net_pkt *pkt,
struct in_addr *request_ip,
struct in_addr *current_ip)
{
bool is_ipv4_ll_used = false;
struct arp_entry *entry;
struct in_addr *addr;
if (!pkt || !pkt->buffer) {
return NULL;
}
if (IS_ENABLED(CONFIG_NET_IPV4_AUTO)) {
is_ipv4_ll_used = net_ipv4_is_ll_addr((struct in_addr *)
&NET_IPV4_HDR(pkt)->src) ||
net_ipv4_is_ll_addr((struct in_addr *)
&NET_IPV4_HDR(pkt)->dst);
}
/* Is the destination in the local network, if not route via
* the gateway address.
*/
if (!current_ip && !is_ipv4_ll_used &&
!net_if_ipv4_addr_mask_cmp(net_pkt_iface(pkt), request_ip)) {
struct net_if_ipv4 *ipv4 = net_pkt_iface(pkt)->config.ip.ipv4;
if (ipv4) {
addr = &ipv4->gw;
if (net_ipv4_is_addr_unspecified(addr)) {
NET_ERR("Gateway not set for iface %p",
net_pkt_iface(pkt));
return NULL;
}
} else {
addr = request_ip;
}
} else {
addr = request_ip;
}
k_mutex_lock(&arp_mutex, K_FOREVER);
/* If the destination address is already known, we do not need
* to send any ARP packet.
*/
entry = arp_entry_find_move_first(net_pkt_iface(pkt), addr);
if (!entry) {
struct net_pkt *req;
entry = arp_entry_find_pending(net_pkt_iface(pkt), addr);
if (!entry) {
/* No pending, let's try to get a new entry */
entry = arp_entry_get_free();
if (!entry) {
/* Then let's take one from table? */
entry = arp_entry_get_last_from_table();
}
} else {
/* There is a pending ARP request already, check if this packet is already
* in the pending list and if so, resend the request, otherwise just
* append the packet to the request fifo list.
*/
if (!net_pkt_ipv4_auto(pkt) &&
k_queue_unique_append(&entry->pending_queue._queue,
net_pkt_ref(pkt))) {
k_mutex_unlock(&arp_mutex);
return NULL;
}
entry = NULL;
}
req = arp_prepare(net_pkt_iface(pkt), addr, entry, pkt,
current_ip);
if (!entry) {
/* We cannot send the packet, the ARP cache is full
* or there is already a pending query to this IP
* address, so this packet must be discarded.
*/
NET_DBG("Resending ARP %p", req);
}
if (!req && entry) {
/* Add the arp entry back to arp_free_entries, to avoid the
* arp entry is leak due to ARP packet allocated failed.
*/
sys_slist_prepend(&arp_free_entries, &entry->node);
}
k_mutex_unlock(&arp_mutex);
return req;
}
k_mutex_unlock(&arp_mutex);
net_pkt_lladdr_src(pkt)->addr =
(uint8_t *)net_if_get_link_addr(entry->iface)->addr;
net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr);
net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)&entry->eth;
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
NET_DBG("ARP using ll %s for IP %s",
net_sprint_ll_addr(net_pkt_lladdr_dst(pkt)->addr,
sizeof(struct net_eth_addr)),
net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst));
return pkt;
}
static void arp_gratuitous(struct net_if *iface,
struct in_addr *src,
struct net_eth_addr *hwaddr)
{
sys_snode_t *prev = NULL;
struct arp_entry *entry;
entry = arp_entry_find(&arp_table, iface, src, &prev);
if (entry) {
NET_DBG("Gratuitous ARP hwaddr %s -> %s",
net_sprint_ll_addr((const uint8_t *)&entry->eth,
sizeof(struct net_eth_addr)),
net_sprint_ll_addr((const uint8_t *)hwaddr,
sizeof(struct net_eth_addr)));
memcpy(&entry->eth, hwaddr, sizeof(struct net_eth_addr));
}
}
void net_arp_update(struct net_if *iface,
struct in_addr *src,
struct net_eth_addr *hwaddr,
bool gratuitous,
bool force)
{
struct arp_entry *entry;
struct net_pkt *pkt;
NET_DBG("iface %d (%p) src %s", net_if_get_by_iface(iface), iface,
net_sprint_ipv4_addr(src));
net_if_tx_lock(iface);
k_mutex_lock(&arp_mutex, K_FOREVER);
entry = arp_entry_get_pending(iface, src);
if (!entry) {
if (IS_ENABLED(CONFIG_NET_ARP_GRATUITOUS) && gratuitous) {
arp_gratuitous(iface, src, hwaddr);
}
if (force) {
sys_snode_t *prev = NULL;
struct arp_entry *arp_ent;
arp_ent = arp_entry_find(&arp_table, iface, src, &prev);
if (arp_ent) {
memcpy(&arp_ent->eth, hwaddr,
sizeof(struct net_eth_addr));
} else {
/* Add new entry as it was not found and force
* was set.
*/
arp_ent = arp_entry_get_free();
if (!arp_ent) {
/* Then let's take one from table? */
arp_ent = arp_entry_get_last_from_table();
}
if (arp_ent) {
arp_ent->req_start = k_uptime_get_32();
arp_ent->iface = iface;
net_ipaddr_copy(&arp_ent->ip, src);
memcpy(&arp_ent->eth, hwaddr, sizeof(arp_ent->eth));
sys_slist_prepend(&arp_table, &arp_ent->node);
}
}
}
k_mutex_unlock(&arp_mutex);
net_if_tx_unlock(iface);
return;
}
memcpy(&entry->eth, hwaddr, sizeof(struct net_eth_addr));
/* Inserting entry into the table */
sys_slist_prepend(&arp_table, &entry->node);
while (!k_fifo_is_empty(&entry->pending_queue)) {
int ret;
pkt = k_fifo_get(&entry->pending_queue, K_FOREVER);
/* Set the dst in the pending packet */
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
net_pkt_lladdr_dst(pkt)->addr =
(uint8_t *) &NET_ETH_HDR(pkt)->dst.addr;
NET_DBG("iface %d (%p) dst %s pending %p frag %p",
net_if_get_by_iface(iface), iface,
net_sprint_ipv4_addr(&entry->ip),
pkt, pkt->frags);
/* We directly send the packet without first queueing it.
* The pkt has already been queued for sending, once by
* net_if and second time in the ARP queue. We must not
* queue it twice in net_if so that the statistics of
* the pkt are not counted twice and the packet filter
* callbacks are only called once.
*/
ret = net_if_l2(iface)->send(iface, pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
}
k_mutex_unlock(&arp_mutex);
net_if_tx_unlock(iface);
}
static inline struct net_pkt *arp_prepare_reply(struct net_if *iface,
struct net_pkt *req,
struct net_eth_hdr *eth_query,
struct net_eth_addr *dst_addr)
{
struct net_arp_hdr *hdr, *query;
struct net_pkt *pkt;
pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_arp_hdr),
AF_UNSPEC, 0, NET_BUF_TIMEOUT);
if (!pkt) {
return NULL;
}
net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr));
hdr = NET_ARP_HDR(pkt);
query = NET_ARP_HDR(req);
if (IS_ENABLED(CONFIG_NET_VLAN)) {
net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(req));
}
hdr->hwtype = htons(NET_ARP_HTYPE_ETH);
hdr->protocol = htons(NET_ETH_PTYPE_IP);
hdr->hwlen = sizeof(struct net_eth_addr);
hdr->protolen = sizeof(struct in_addr);
hdr->opcode = htons(NET_ARP_REPLY);
memcpy(&hdr->dst_hwaddr.addr, &dst_addr->addr,
sizeof(struct net_eth_addr));
memcpy(&hdr->src_hwaddr.addr, net_if_get_link_addr(iface)->addr,
sizeof(struct net_eth_addr));
net_ipv4_addr_copy_raw(hdr->dst_ipaddr, query->src_ipaddr);
net_ipv4_addr_copy_raw(hdr->src_ipaddr, query->dst_ipaddr);
net_pkt_lladdr_src(pkt)->addr = net_if_get_link_addr(iface)->addr;
net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr);
net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)&hdr->dst_hwaddr.addr;
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
return pkt;
}
static bool arp_hdr_check(struct net_arp_hdr *arp_hdr)
{
if (ntohs(arp_hdr->hwtype) != NET_ARP_HTYPE_ETH ||
ntohs(arp_hdr->protocol) != NET_ETH_PTYPE_IP ||
arp_hdr->hwlen != sizeof(struct net_eth_addr) ||
arp_hdr->protolen != NET_ARP_IPV4_PTYPE_SIZE ||
net_ipv4_is_addr_loopback((struct in_addr *)arp_hdr->src_ipaddr)) {
NET_DBG("DROP: Invalid ARP header");
return false;
}
return true;
}
enum net_verdict net_arp_input(struct net_pkt *pkt,
struct net_eth_hdr *eth_hdr)
{
struct net_eth_addr *dst_hw_addr;
struct net_arp_hdr *arp_hdr;
struct net_pkt *reply;
struct in_addr *addr;
if (net_pkt_get_len(pkt) < (sizeof(struct net_arp_hdr) -
(net_pkt_ip_data(pkt) - (uint8_t *)eth_hdr))) {
NET_DBG("Invalid ARP header (len %zu, min %zu bytes) %p",
net_pkt_get_len(pkt), sizeof(struct net_arp_hdr) -
(net_pkt_ip_data(pkt) - (uint8_t *)eth_hdr), pkt);
return NET_DROP;
}
arp_hdr = NET_ARP_HDR(pkt);
if (!arp_hdr_check(arp_hdr)) {
return NET_DROP;
}
switch (ntohs(arp_hdr->opcode)) {
case NET_ARP_REQUEST:
/* If ARP request sender hw address is our address,
* we must drop the packet.
*/
if (memcmp(&arp_hdr->src_hwaddr,
net_if_get_link_addr(net_pkt_iface(pkt))->addr,
sizeof(struct net_eth_addr)) == 0) {
return NET_DROP;
}
if (IS_ENABLED(CONFIG_NET_ARP_GRATUITOUS)) {
if (memcmp(&eth_hdr->dst,
net_eth_broadcast_addr(),
sizeof(struct net_eth_addr)) == 0 &&
memcmp(&arp_hdr->dst_hwaddr,
net_eth_broadcast_addr(),
sizeof(struct net_eth_addr)) == 0 &&
memcmp(&arp_hdr->dst_ipaddr, &arp_hdr->src_ipaddr,
sizeof(struct in_addr)) == 0) {
/* If the IP address is in our cache,
* then update it here.
*/
net_arp_update(net_pkt_iface(pkt),
(struct in_addr *)arp_hdr->src_ipaddr,
&arp_hdr->src_hwaddr,
true, false);
break;
}
}
/* Discard ARP request if Ethernet address is broadcast
* and Source IP address is Multicast address.
*/
if (memcmp(&eth_hdr->dst, net_eth_broadcast_addr(),
sizeof(struct net_eth_addr)) == 0 &&
net_ipv4_is_addr_mcast((struct in_addr *)arp_hdr->src_ipaddr)) {
NET_DBG("DROP: eth addr is bcast, src addr is mcast");
return NET_DROP;
}
/* Someone wants to know our ll address */
addr = if_get_addr(net_pkt_iface(pkt),
(struct in_addr *)arp_hdr->dst_ipaddr);
if (!addr) {
/* Not for us so drop the packet silently */
return NET_DROP;
}
NET_DBG("ARP request from %s [%s] for %s",
net_sprint_ipv4_addr(&arp_hdr->src_ipaddr),
net_sprint_ll_addr((uint8_t *)&arp_hdr->src_hwaddr,
arp_hdr->hwlen),
net_sprint_ipv4_addr(&arp_hdr->dst_ipaddr));
/* Update the ARP cache if the sender MAC address has
* changed. In this case the target MAC address is all zeros
* and the target IP address is our address.
*/
if (net_eth_is_addr_unspecified(&arp_hdr->dst_hwaddr)) {
NET_DBG("Updating ARP cache for %s [%s] iface %d",
net_sprint_ipv4_addr(&arp_hdr->src_ipaddr),
net_sprint_ll_addr((uint8_t *)&arp_hdr->src_hwaddr,
arp_hdr->hwlen),
net_if_get_by_iface(net_pkt_iface(pkt)));
net_arp_update(net_pkt_iface(pkt),
(struct in_addr *)arp_hdr->src_ipaddr,
&arp_hdr->src_hwaddr,
false, true);
dst_hw_addr = &arp_hdr->src_hwaddr;
} else {
dst_hw_addr = &eth_hdr->src;
}
/* Send reply */
reply = arp_prepare_reply(net_pkt_iface(pkt), pkt, eth_hdr,
dst_hw_addr);
if (reply) {
net_if_queue_tx(net_pkt_iface(reply), reply);
} else {
NET_DBG("Cannot send ARP reply");
}
break;
case NET_ARP_REPLY:
if (net_ipv4_is_my_addr((struct in_addr *)arp_hdr->dst_ipaddr)) {
net_arp_update(net_pkt_iface(pkt),
(struct in_addr *)arp_hdr->src_ipaddr,
&arp_hdr->src_hwaddr,
false, false);
}
break;
}
net_pkt_unref(pkt);
return NET_OK;
}
void net_arp_clear_cache(struct net_if *iface)
{
sys_snode_t *prev = NULL;
struct arp_entry *entry, *next;
NET_DBG("Flushing ARP table");
k_mutex_lock(&arp_mutex, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&arp_table, entry, next, node) {
if (iface && iface != entry->iface) {
prev = &entry->node;
continue;
}
arp_entry_cleanup(entry, false);
sys_slist_remove(&arp_table, prev, &entry->node);
sys_slist_prepend(&arp_free_entries, &entry->node);
}
prev = NULL;
NET_DBG("Flushing ARP pending requests");
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&arp_pending_entries,
entry, next, node) {
if (iface && iface != entry->iface) {
prev = &entry->node;
continue;
}
arp_entry_cleanup(entry, true);
sys_slist_remove(&arp_pending_entries, prev, &entry->node);
sys_slist_prepend(&arp_free_entries, &entry->node);
}
if (sys_slist_is_empty(&arp_pending_entries)) {
k_work_cancel_delayable(&arp_request_timer);
}
k_mutex_unlock(&arp_mutex);
}
int net_arp_clear_pending(struct net_if *iface, struct in_addr *dst)
{
struct arp_entry *entry = arp_entry_find_pending(iface, dst);
if (!entry) {
return -ENOENT;
}
arp_entry_cleanup(entry, true);
return 0;
}
int net_arp_foreach(net_arp_cb_t cb, void *user_data)
{
int ret = 0;
struct arp_entry *entry;
k_mutex_lock(&arp_mutex, K_FOREVER);
SYS_SLIST_FOR_EACH_CONTAINER(&arp_table, entry, node) {
ret++;
cb(entry, user_data);
}
k_mutex_unlock(&arp_mutex);
return ret;
}
void net_arp_init(void)
{
int i;
if (arp_cache_initialized) {
return;
}
sys_slist_init(&arp_free_entries);
sys_slist_init(&arp_pending_entries);
sys_slist_init(&arp_table);
for (i = 0; i < CONFIG_NET_ARP_TABLE_SIZE; i++) {
/* Inserting entry as free with initialised packet queue */
k_fifo_init(&arp_entries[i].pending_queue);
sys_slist_prepend(&arp_free_entries, &arp_entries[i].node);
}
k_work_init_delayable(&arp_request_timer, arp_request_timeout);
k_mutex_init(&arp_mutex);
arp_cache_initialized = true;
}