Support to ARP Address Conflict Detection

Support to ARP Address Conflict Detection

Signed-off-by: wangchen <wangchen41@xiaomi.com>
This commit is contained in:
wangchen 2024-01-22 10:20:46 +08:00 committed by Alan Carvalho de Assis
parent a188cf3480
commit 204f4a18a0
10 changed files with 532 additions and 3 deletions

View file

@ -58,6 +58,7 @@
#include <netinet/in.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/wqueue.h>
/****************************************************************************
* Pre-processor Definitions
@ -245,6 +246,41 @@ struct ipv6_stats_s
#endif /* CONFIG_NET_IPv6 */
#endif /* CONFIG_NET_STATISTICS */
#ifdef CONFIG_NET_ARP_ACD
#define ARP_ACD_TMR_INTERVAL 100 /* milliseconds */
#define ARP_ACD_TICKS_PER_SECOND (1000 / ARP_ACD_TMR_INTERVAL)
/* RFC 5227 Constants */
#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
/* arp acd entry states */
enum arp_acd_state_e
{
ARP_ACD_STATE_INIT = 0,
ARP_ACD_STATE_ANNOUNCING = 1,
ARP_ACD_STATE_FINISH = 2
};
#define ARP_ACD_ADDRESS_NO_CONFLICT 0
#define ARP_ACD_ADDRESS_CONFLICT 1
struct arp_acd_s
{
enum arp_acd_state_e state; /* current arp_acd_s status */
int sendnum; /* sent number of probes or announces, dependent on state */
bool conflict_flag; /* arp address conflict flag */
bool need_announce; /* need to send arp announce packet */
uint32_t ttw; /* ticks to wait */
clock_t lastconflict; /* last conflict timestamp */
struct work_s work; /* For deferred timeout operations */
};
#endif /* CONFIG_NET_ARP_ACD */
/****************************************************************************
* Public Data
****************************************************************************/

View file

@ -320,6 +320,9 @@ struct net_driver_s
in_addr_t d_ipaddr; /* Host IPv4 address assigned to the network interface */
in_addr_t d_draddr; /* Default router IP address */
in_addr_t d_netmask; /* Network subnet mask */
#ifdef CONFIG_NET_ARP_ACD
struct arp_acd_s d_acd; /* ipv4 acd entry */
#endif /* CONFIG_NET_ARP_ACD */
#endif
#ifdef CONFIG_NET_IPv6

View file

@ -76,5 +76,12 @@ config NET_ARP_DUMP
---help---
Dump ARP packets to the SYSLOG device.
config NET_ARP_ACD
bool "Support of ARP address Address Conflict Detection"
default n
depends on NET_ARP_SEND
---help---
Enable Support of ARP address Address Conflict Detection
endif # NET_ARP
endmenu # ARP Configuration

View file

@ -35,6 +35,10 @@ ifeq ($(CONFIG_NET_ARP_DUMP),y)
NET_CSRCS += arp_dump.c
endif
ifeq ($(CONFIG_NET_ARP_ACD),y)
NET_CSRCS += arp_acd.c
endif
# Include arp build support
DEPPATH += --dep-path arp

View file

@ -128,9 +128,12 @@ struct arp_iphdr_s
* operated upon from the network driver poll.
*/
typedef CODE void (*arp_send_finish_cb_t)(FAR struct net_driver_s *dev,
int result);
struct arp_send_s
{
FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */
FAR arp_send_finish_cb_t finish_cb; /* Reference to send finish callback */
sem_t snd_sem; /* Used to wake up the waiting thread */
uint8_t snd_retries; /* Retry count */
volatile bool snd_sent; /* True: if request sent */
@ -275,6 +278,33 @@ int arp_send(in_addr_t ipaddr);
# define arp_send(i) (0)
#endif
/****************************************************************************
* Name: arp_send_async
*
* Description:
* The arp_send_async() call may be to send an ARP request asyncly to
* resolve an IPv4 address.
*
* Input Parameters:
* ipaddr The IP address to be queried.
* cb The callback when ARP send is finished, should not be NULL.
*
* Returned Value:
* Zero (OK) is returned on success the arp been sent to the driver.
* On error a negated errno value is returned:
*
* -ETIMEDOUT: The number or retry counts has been exceed.
* -EHOSTUNREACH: Could not find a route to the host
*
* Assumptions:
* This function is called from the normal tasking context.
*
****************************************************************************/
#ifdef CONFIG_NET_ARP_SEND
int arp_send_async(in_addr_t ipaddr, arp_send_finish_cb_t cb);
#endif
/****************************************************************************
* Name: arp_poll
*
@ -523,6 +553,58 @@ void arp_dump(FAR struct arp_hdr_s *arp);
# define arp_dump(arp)
#endif
#ifdef CONFIG_NET_ARP_ACD
/****************************************************************************
* Name: arp_acd_update
*
* Description:
* interface of ARP Address Conflict Detection monitor
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_update(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: arp_acd_set_addr
*
* Description:
* setting address interface of ARP Address Conflict Detection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_set_addr(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: arp_acd_setup
*
* Description:
* set up interface of ARP Address Conflict Detection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_setup(FAR struct net_driver_s *dev);
#endif /* CONFIG_NET_ARP_ACD */
#else /* CONFIG_NET_ARP */
/* If ARP is disabled, stub out all ARP interfaces */

281
net/arp/arp_acd.c Normal file
View file

@ -0,0 +1,281 @@
/****************************************************************************
* net/arp/arp_acd.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <stdlib.h>
#include <unistd.h>
#include <nuttx/net/ethernet.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/semaphore.h>
#include "arp/arp.h"
#include "netlink/netlink.h"
#include "utils/utils.h"
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void arp_acd_try_announce(FAR void *net_dev);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: arp_acd_arrange_announce
*
* Description:
* creat work_queue to send ARP announce
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
static void arp_acd_arrange_announce(FAR struct net_driver_s *dev)
{
if (dev->d_acd.need_announce == true)
{
return;
}
if (!work_available(&dev->d_acd.work))
{
nerr("ERROR work unavailable \n");
return;
}
int ret = work_queue(LPWORK, &dev->d_acd.work, arp_acd_try_announce,
(FAR void *)dev, DSEC2TICK(dev->d_acd.ttw));
if (ret != OK)
{
nerr("ERROR ret %d \n", ret);
}
}
/****************************************************************************
* Name: arp_acd_send_finish
*
* Description:
* send finish process of ARP Address Conflict Detection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* result - arp send result
*
* Returned Value:
* none
*
****************************************************************************/
static void arp_acd_send_finish(FAR struct net_driver_s *dev, int result)
{
if (result < 0)
{
nerr("ERROR: arp_send result: %d\n", result);
}
else
{
arp_acd_arrange_announce(dev);
}
}
/****************************************************************************
* Name: arp_acd_try_announce
*
* Description:
* process status of ARP Address Conflict Detection
*
* Input Parameters:
* net_dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
static void arp_acd_try_announce(FAR void *net_dev)
{
FAR struct net_driver_s *dev = net_dev;
if (dev == NULL || dev->d_acd.state != ARP_ACD_STATE_ANNOUNCING)
{
return;
}
/* arp_acd_announce */
arp_send_async(dev->d_ipaddr, arp_acd_send_finish);
dev->d_acd.sendnum++;
if (dev->d_acd.sendnum >= ANNOUNCE_NUM)
{
dev->d_acd.sendnum = 0;
dev->d_acd.ttw = 0;
dev->d_acd.state = ARP_ACD_STATE_FINISH;
}
else
{
dev->d_acd.ttw = ANNOUNCE_INTERVAL * ARP_ACD_TICKS_PER_SECOND;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: arp_acd_update
*
* Description:
* interface of ARP Address Conflict Detection monitor
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_update(FAR struct net_driver_s *dev)
{
FAR struct arp_hdr_s *arp = ARPBUF;
clock_t now = clock_systime_ticks();
if (dev->d_acd.conflict_flag == ARP_ACD_ADDRESS_CONFLICT)
{
return;
}
if (!net_ipv4addr_hdrcmp(arp->ah_sipaddr, &dev->d_ipaddr) ||
(memcmp(arp->ah_shwaddr, dev->d_mac.ether.ether_addr_octet,
sizeof(arp->ah_shwaddr)) == 0))
{
return;
}
if ((dev->d_acd.lastconflict > 0) &&
(now - dev->d_acd.lastconflict) <
DSEC2TICK(DEFEND_INTERVAL * ARP_ACD_TICKS_PER_SECOND))
{
nerr("ERROR: detect conflict again \n");
dev->d_acd.lastconflict = 0;
dev->d_acd.conflict_flag = ARP_ACD_ADDRESS_CONFLICT;
if (dev->d_acd.state != ARP_ACD_STATE_ANNOUNCING)
{
dev->d_acd.state = ARP_ACD_STATE_INIT;
dev->d_acd.sendnum = 0;
dev->d_acd.ttw = 0;
}
}
else
{
nerr("ERROR: detect conflict \n");
dev->d_acd.lastconflict = now;
if (dev->d_acd.state != ARP_ACD_STATE_ANNOUNCING)
{
arp_acd_arrange_announce(dev);
dev->d_acd.state = ARP_ACD_STATE_ANNOUNCING;
dev->d_acd.sendnum = 0;
dev->d_acd.ttw =
ANNOUNCE_WAIT * ARP_ACD_TICKS_PER_SECOND;
}
}
}
/****************************************************************************
* Name: arp_acd_setup
*
* Description:
* set up interface of ARP Address Conflict Detection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_setup(FAR struct net_driver_s *dev)
{
if (dev->d_acd.need_announce == false)
{
return;
}
dev->d_acd.state = ARP_ACD_STATE_ANNOUNCING;
dev->d_acd.sendnum = 0;
dev->d_acd.ttw = 0;
dev->d_acd.conflict_flag = ARP_ACD_ADDRESS_NO_CONFLICT;
dev->d_acd.lastconflict = 0;
dev->d_acd.need_announce = false;
arp_acd_arrange_announce(dev);
}
/****************************************************************************
* Name: arp_acd_set_addr
*
* Description:
* setting address interface of ARP Address Conflict Detection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* none
*
****************************************************************************/
void arp_acd_set_addr(FAR struct net_driver_s *dev)
{
if (!net_ipv4addr_cmp(dev->d_ipaddr, INADDR_ANY))
{
dev->d_acd.need_announce = true;
if (IFF_IS_UP(dev->d_flags))
{
arp_acd_setup(dev);
}
}
else
{
dev->d_acd.need_announce = false;
dev->d_acd.state = ARP_ACD_STATE_INIT;
}
}

View file

@ -98,6 +98,11 @@ static int arp_in(FAR struct net_driver_s *dev)
dev->d_len = 0;
ipaddr = net_ip4addr_conv32(arp->ah_dipaddr);
#ifdef CONFIG_NET_ARP_ACD
arp_acd_update(dev);
#endif /* CONFIG_NET_ARP_ACD */
switch (arp->ah_opcode)
{
case HTONS(ARP_REQUEST):

View file

@ -31,6 +31,7 @@
#include <netinet/in.h>
#include <net/if.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ip.h>
@ -50,7 +51,8 @@
* Name: arp_send_terminate
****************************************************************************/
static void arp_send_terminate(FAR struct arp_send_s *state, int result)
static void arp_send_terminate(FAR struct net_driver_s *dev,
FAR struct arp_send_s *state, int result)
{
/* Don't allow any further call backs. */
@ -63,6 +65,14 @@ static void arp_send_terminate(FAR struct arp_send_s *state, int result)
/* Wake up the waiting thread */
nxsem_post(&state->snd_sem);
if (state->finish_cb != NULL)
{
nxsem_destroy(&state->snd_sem);
arp_callback_free(dev, state->snd_cb);
state->finish_cb(dev, result);
kmm_free(state);
}
}
/****************************************************************************
@ -93,7 +103,7 @@ static uint16_t arp_send_eventhandler(FAR struct net_driver_s *dev,
if ((flags & NETDEV_DOWN) != 0)
{
nerr("ERROR: Interface is down\n");
arp_send_terminate(state, -ENETUNREACH);
arp_send_terminate(dev, state, -ENETUNREACH);
return flags;
}
@ -130,7 +140,7 @@ static uint16_t arp_send_eventhandler(FAR struct net_driver_s *dev,
/* Don't allow any further call backs. */
arp_send_terminate(state, OK);
arp_send_terminate(dev, state, OK);
}
return flags;
@ -319,6 +329,7 @@ int arp_send(in_addr_t ipaddr)
state.snd_cb->flags = (ARP_POLL | NETDEV_DOWN);
state.snd_cb->priv = (FAR void *)&state;
state.snd_cb->event = arp_send_eventhandler;
state.finish_cb = NULL;
/* Notify the device driver that new TX data is available. */
@ -385,4 +396,82 @@ errout:
return ret;
}
/****************************************************************************
* Name: arp_send_async
*
* Description:
* The arp_send_async() call may be to send an ARP request asyncly to
* resolve an IPv4 address.
*
* Input Parameters:
* ipaddr The IP address to be queried.
* cb The callback when ARP send is finished, should not be NULL.
*
* Returned Value:
* Zero (OK) is returned on success the arp been sent to the driver.
* On error a negated errno value is returned:
*
* -ETIMEDOUT: The number or retry counts has been exceed.
* -EHOSTUNREACH: Could not find a route to the host
*
* Assumptions:
* This function is called from the normal tasking context.
*
****************************************************************************/
int arp_send_async(in_addr_t ipaddr, arp_send_finish_cb_t cb)
{
FAR struct net_driver_s *dev;
FAR struct arp_send_s *state = kmm_zalloc(sizeof(struct arp_send_s));
int ret = 0;
if (!state)
{
nerr("ERROR: %s \n", ENOMEM_STR);
ret = -ENOMEM;
goto errout;
}
dev = netdev_findby_ripv4addr(INADDR_ANY, ipaddr);
if (!dev)
{
nerr("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr);
ret = -EHOSTUNREACH;
goto errout;
}
net_lock();
state->snd_cb = arp_callback_alloc(dev);
if (!state->snd_cb)
{
nerr("ERROR: Failed to allocate a callback\n");
ret = -ENOMEM;
goto errout_with_lock;
}
nxsem_init(&state->snd_sem, 0, 0); /* Doesn't really fail */
state->snd_ipaddr = ipaddr; /* IP address to query */
/* Remember the routing device name */
strlcpy((FAR char *)state->snd_ifname,
(FAR const char *)dev->d_ifname, IFNAMSIZ);
/* Arm/re-arm the callback */
state->snd_cb->flags = (ARP_POLL | NETDEV_DOWN);
state->snd_cb->priv = (FAR void *)state;
state->snd_cb->event = arp_send_eventhandler;
state->finish_cb = cb;
/* Notify the device driver that new TX data is available. */
netdev_txnotify_dev(dev);
errout_with_lock:
net_unlock();
errout:
return ret;
}
#endif /* CONFIG_NET_ARP_SEND */

View file

@ -982,6 +982,11 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
/* Yes.. bring the interface up */
ret = netdev_ifup(dev);
#ifdef CONFIG_NET_ARP_ACD
/* having address then start acd */
arp_acd_setup(dev);
#endif /* CONFIG_NET_ARP_ACD */
}
/* Is this a request to take the interface down? */
@ -1080,9 +1085,19 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
#ifdef CONFIG_NET_IPv4
if (psock->s_domain != PF_INET6)
{
if (net_ipv4addr_cmp(dev->d_ipaddr,
((FAR struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr))
{
break;
}
ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET,
&dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
#ifdef CONFIG_NET_ARP_ACD
arp_acd_set_addr(dev);
#endif /* CONFIG_NET_ARP_ACD */
}
#endif

View file

@ -293,6 +293,13 @@ static int netprocfs_inet4addresses(FAR struct netprocfs_file_s *netfile)
len += snprintf(&netfile->line[len], NET_LINELEN - len,
"\tinet addr:%s ", inet_ntoa_r(addr, inetaddr,
sizeof(inetaddr)));
#ifdef CONFIG_NET_ARP_ACD
if (dev->d_acd.conflict_flag == ARP_ACD_ADDRESS_CONFLICT)
{
len += snprintf(&netfile->line[len], NET_LINELEN - len,
"(conflict!) ");
}
#endif
/* Show the IPv4 default router address */