diff --git a/include/nuttx/net/ip.h b/include/nuttx/net/ip.h index ec97434a210..6e6db2c0daa 100644 --- a/include/nuttx/net/ip.h +++ b/include/nuttx/net/ip.h @@ -58,6 +58,7 @@ #include #include +#include /**************************************************************************** * 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 ****************************************************************************/ diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 647e6d73057..594e03409a4 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -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 diff --git a/net/arp/Kconfig b/net/arp/Kconfig index 01a8032065c..9b3286fed60 100644 --- a/net/arp/Kconfig +++ b/net/arp/Kconfig @@ -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 diff --git a/net/arp/Make.defs b/net/arp/Make.defs index a48d0078985..c090646baf8 100644 --- a/net/arp/Make.defs +++ b/net/arp/Make.defs @@ -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 diff --git a/net/arp/arp.h b/net/arp/arp.h index 7e2c9887c28..03648007f45 100644 --- a/net/arp/arp.h +++ b/net/arp/arp.h @@ -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 */ diff --git a/net/arp/arp_acd.c b/net/arp/arp_acd.c new file mode 100644 index 00000000000..20a182e8d86 --- /dev/null +++ b/net/arp/arp_acd.c @@ -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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; + } +} diff --git a/net/arp/arp_input.c b/net/arp/arp_input.c index 03892bb1dca..a76f48fa613 100644 --- a/net/arp/arp_input.c +++ b/net/arp/arp_input.c @@ -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): diff --git a/net/arp/arp_send.c b/net/arp/arp_send.c index bed3b589664..6eb568b10b1 100644 --- a/net/arp/arp_send.c +++ b/net/arp/arp_send.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -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 */ diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c index 54538e79591..56129d6ae6b 100644 --- a/net/netdev/netdev_ioctl.c +++ b/net/netdev/netdev_ioctl.c @@ -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 diff --git a/net/procfs/netdev_statistics.c b/net/procfs/netdev_statistics.c index e62fd59830c..aeaa43d3e33 100644 --- a/net/procfs/netdev_statistics.c +++ b/net/procfs/netdev_statistics.c @@ -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 */