net/arp: limit arp request when arp is in progress to prevent ARP flooding

Wait a fixed number of milliseconds before sending a retry ARP request for the same destination if the IP address mapping is not exist to prevent ARP flooding

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
This commit is contained in:
wenquan1
2025-07-16 15:28:18 +08:00
committed by Xiang Xiao
parent 6b30226c0e
commit 4ae08155f9
4 changed files with 88 additions and 6 deletions
+8
View File
@@ -28,6 +28,14 @@ config NET_ARP_MAXAGE
The maximum age of ARP table entries measured in deciseconds. The The maximum age of ARP table entries measured in deciseconds. The
default value of 120 corresponds to 20 minutes (BSD default). default value of 120 corresponds to 20 minutes (BSD default).
config NET_ARP_MAXAGE_UNREACHABLE
int "Max unreachable ARP entry age"
default 1
---help---
The maximum age of unreachable ARP table entries measured in
deciseconds. During unreachable period not send a retry ARP
request for the same destination.
config NET_ARP_IPIN config NET_ARP_IPIN
bool "ARP address harvesting" bool "ARP address harvesting"
default n default n
+17
View File
@@ -262,6 +262,23 @@ void arp_out(FAR struct net_driver_s *dev)
ninfo("ARP request for IP %08lx\n", (unsigned long)ipaddr); ninfo("ARP request for IP %08lx\n", (unsigned long)ipaddr);
if (ret == -EINPROGRESS)
{
/* The destination address was not in our ARP table, and
* the last arp request is in progress, directly drop the packet
* to prevent arp flood.
*/
dev->d_len = 0;
return;
}
/* MAC address marked with all zeros to limit concurrent task
* send ARP request for same destination.
*/
arp_update(dev, ipaddr, NULL);
/* The destination address was not in our ARP table, so we overwrite /* The destination address was not in our ARP table, so we overwrite
* the IP packet with an ARP request. * the IP packet with an ARP request.
*/ */
+24 -4
View File
@@ -180,6 +180,7 @@ int arp_send(in_addr_t ipaddr)
FAR struct net_driver_s *dev; FAR struct net_driver_s *dev;
struct arp_notify_s notify; struct arp_notify_s notify;
struct arp_send_s state; struct arp_send_s state;
bool sending = false;
int ret; int ret;
/* First check if destination is a local broadcast. */ /* First check if destination is a local broadcast. */
@@ -306,12 +307,12 @@ int arp_send(in_addr_t ipaddr)
*/ */
netdev_lock(dev); netdev_lock(dev);
if (arp_find(ipaddr, NULL, dev, true) >= 0) ret = arp_find(ipaddr, NULL, dev, true);
if (ret >= 0 || ret == -ENETUNREACH)
{ {
/* We have it! Break out with success */ /* We have it! Break out with ret value */
netdev_unlock(dev); netdev_unlock(dev);
ret = OK;
break; break;
} }
@@ -319,6 +320,15 @@ int arp_send(in_addr_t ipaddr)
arp_wait_setup(ipaddr, &notify); arp_wait_setup(ipaddr, &notify);
if (ret == -EINPROGRESS && !sending)
{
/* ARP request for the same destination is in progress, directly
* wait arp response notify.
*/
goto wait;
}
/* Allocate resources to receive a callback. This and the following /* Allocate resources to receive a callback. This and the following
* initialization is performed with the network lock because we don't * initialization is performed with the network lock because we don't
* want anything to happen until we are ready. * want anything to happen until we are ready.
@@ -351,6 +361,16 @@ int arp_send(in_addr_t ipaddr)
netdev_txnotify_dev(dev); netdev_txnotify_dev(dev);
/* MAC address marked with all zeros to limit concurrent task
* send ARP request for same destination.
*/
if (state.snd_retries == 0)
{
arp_update(dev, ipaddr, NULL);
sending = true;
}
/* Wait for the send to complete or an error to occur. /* Wait for the send to complete or an error to occur.
* net_sem_wait will also terminate if a signal is received. * net_sem_wait will also terminate if a signal is received.
*/ */
@@ -381,6 +401,7 @@ int arp_send(in_addr_t ipaddr)
/* Now wait for response to the ARP response to be received. */ /* Now wait for response to the ARP response to be received. */
wait:
ret = arp_wait(&notify, CONFIG_ARP_SEND_DELAYMSEC); ret = arp_wait(&notify, CONFIG_ARP_SEND_DELAYMSEC);
/* arp_wait will return OK if and only if the matching ARP response /* arp_wait will return OK if and only if the matching ARP response
@@ -406,7 +427,6 @@ timeout:
nxsem_destroy(&state.snd_sem); nxsem_destroy(&state.snd_sem);
arp_callback_free(dev, state.snd_cb); arp_callback_free(dev, state.snd_cb);
return ret; return ret;
} }
+39 -2
View File
@@ -72,6 +72,8 @@
****************************************************************************/ ****************************************************************************/
#define ARP_MAXAGE_TICK SEC2TICK(10 * CONFIG_NET_ARP_MAXAGE) #define ARP_MAXAGE_TICK SEC2TICK(10 * CONFIG_NET_ARP_MAXAGE)
#define ARP_MAXAGE_UNREACHABLE_TICK SEC2TICK(10 * CONFIG_NET_ARP_MAXAGE_UNREACHABLE)
#define ARP_INPROGRESS_TICK MSEC2TICK(CONFIG_ARP_SEND_MAXTRIES * CONFIG_ARP_SEND_DELAYMSEC)
/**************************************************************************** /****************************************************************************
* Private Types * Private Types
@@ -91,6 +93,13 @@ struct arp_table_info_s
static struct arp_entry_s g_arptable[CONFIG_NET_ARPTAB_SIZE]; static struct arp_entry_s g_arptable[CONFIG_NET_ARPTAB_SIZE];
static const struct ether_addr g_zero_ethaddr =
{
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}
};
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@@ -322,6 +331,11 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr,
} }
} }
if (ethaddr == NULL)
{
ethaddr = g_zero_ethaddr.ether_addr_octet;
}
/* When overwrite old entry, notify old entry RTM_DELNEIGH */ /* When overwrite old entry, notify old entry RTM_DELNEIGH */
#ifdef CONFIG_NETLINK_ROUTE #ifdef CONFIG_NETLINK_ROUTE
@@ -422,6 +436,29 @@ int arp_find(in_addr_t ipaddr, FAR uint8_t *ethaddr,
tabptr = arp_lookup(ipaddr, dev, check_expiry); tabptr = arp_lookup(ipaddr, dev, check_expiry);
if (tabptr != NULL) if (tabptr != NULL)
{ {
/* Addresses that have failed to be searched will return a special
* error code so that the upper layer can return faster.
*/
if (memcmp(&tabptr->at_ethaddr, &g_zero_ethaddr,
sizeof(tabptr->at_ethaddr)) == 0)
{
clock_t elapsed;
elapsed = clock_systime_ticks() - tabptr->at_time;
if (elapsed <= ARP_INPROGRESS_TICK)
{
return -EINPROGRESS;
}
else if (elapsed <= ARP_MAXAGE_UNREACHABLE_TICK)
{
return -ENETUNREACH;
}
else
{
return -ENOENT;
}
}
/* Yes.. return the Ethernet MAC address if the caller has provided a /* Yes.. return the Ethernet MAC address if the caller has provided a
* non-NULL address in 'ethaddr'. * non-NULL address in 'ethaddr'.
*/ */
@@ -431,8 +468,8 @@ int arp_find(in_addr_t ipaddr, FAR uint8_t *ethaddr,
memcpy(ethaddr, &tabptr->at_ethaddr, ETHER_ADDR_LEN); memcpy(ethaddr, &tabptr->at_ethaddr, ETHER_ADDR_LEN);
} }
/* Return success in any case meaning that a valid Ethernet MAC /* Return success meaning that a valid Ethernet MAC address mapping
* address mapping is available for the IP address. * is available for the IP address.
*/ */
return OK; return OK;