From 8ecb84ed8941bc8024825c3105c198379c8a5592 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 27 May 2015 09:26:00 -0600 Subject: [PATCH] Improve icmp handling if MULTINIC enabled. From Max Neklyudov --- include/nuttx/net/netdev.h | 13 ++++++++++++ net/devif/devif.h | 5 ++++- net/icmp/icmp_input.c | 12 ++++------- net/icmp/icmp_ping.c | 39 ++++++++++++++++++++++++++++++------ net/icmp/icmp_poll.c | 2 +- net/netdev/netdev.h | 1 + net/netdev/netdev_ioctl.c | 8 +++++++- net/netdev/netdev_register.c | 4 ++++ net/netdev/netdev_txnotify.c | 31 ++++++++++++++++++++++++++++ 9 files changed, 98 insertions(+), 17 deletions(-) diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 1b6a10bd5c..8d29384c6b 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -70,6 +70,8 @@ * Public Types ****************************************************************************/ +struct devif_callback_s; /* Forward reference */ + /* This structure collects information that is specific to a specific network * interface driver. If the hardware platform supports only a single instance * of this structure. @@ -190,6 +192,17 @@ struct net_driver_s sq_queue_t grplist; #endif + /* Application callbacks: + * + * Network device event handlers are retained in a 'list' and are called + * for events specified in the flags set within struct devif_callback_s. + * The following network event flags may be specified: + * + * NETDEV_DOWN - The network is down + */ + + FAR struct devif_callback_s *d_callbacks; + /* Driver callbacks */ int (*d_ifup)(FAR struct net_driver_s *dev); diff --git a/net/devif/devif.h b/net/devif/devif.h index 50389c0684..3a96e39061 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -115,6 +115,7 @@ * OUT: Cleared (only) by the socket layer logic to indicate * that the reply was processed, suppressing further * attempts to process the reply. + * NETDEV_DOWN: IN: The network device has been taken down. */ #define TCP_ACKDATA (1 << 0) @@ -138,8 +139,10 @@ #define TCP_TIMEDOUT (1 << 9) #define ICMP_ECHOREPLY (1 << 10) #define ICMPv6_ECHOREPLY ICMP_ECHOREPLY +#define NETDEV_DOWN (1 << 11) -#define TCP_CONN_EVENTS (TCP_CLOSE | TCP_ABORT | TCP_CONNECTED | TCP_TIMEDOUT) +#define TCP_CONN_EVENTS (TCP_CLOSE | TCP_ABORT | TCP_CONNECTED | \ + TCP_TIMEDOUT | NETDEV_DOWN) /* IPv4/IPv6 Helpers */ diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index db39e8356d..e58b52e278 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -69,17 +69,13 @@ #define ICMPBUF ((struct icmp_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) /**************************************************************************** - * Public Variables + * Public Data ****************************************************************************/ /**************************************************************************** - * Private Variables + * Private Data ****************************************************************************/ -#ifdef CONFIG_NET_ICMP_PING -FAR struct devif_callback_s *g_icmp_echocallback = NULL; -#endif - /**************************************************************************** * Private Functions ****************************************************************************/ @@ -182,9 +178,9 @@ void icmp_input(FAR struct net_driver_s *dev) */ #ifdef CONFIG_NET_ICMP_PING - else if (picmp->type == ICMP_ECHO_REPLY && g_icmp_echocallback) + else if (picmp->type == ICMP_ECHO_REPLY && dev->d_callbacks) { - (void)devif_callback_execute(dev, picmp, ICMP_ECHOREPLY, g_icmp_echocallback); + (void)devif_callback_execute(dev, picmp, ICMP_ECHOREPLY, dev->d_callbacks); } #endif diff --git a/net/icmp/icmp_ping.c b/net/icmp/icmp_ping.c index 76d7e8a61e..e15a117f0f 100644 --- a/net/icmp/icmp_ping.c +++ b/net/icmp/icmp_ping.c @@ -70,8 +70,8 @@ /* Allocate a new ICMP data callback */ -#define icmp_callback_alloc() devif_callback_alloc(&g_icmp_echocallback) -#define icmp_callback_free(cb) devif_callback_free(cb, &g_icmp_echocallback) +#define icmp_callback_alloc() devif_callback_alloc(&dev->d_callbacks) +#define icmp_callback_free(cb) devif_callback_free(cb, &dev->d_callbacks) /**************************************************************************** * Private Types @@ -87,7 +87,7 @@ struct icmp_ping_s int png_result; /* 0: success; <0:negated errno on fail */ in_addr_t png_addr; /* The peer to be ping'ed */ uint16_t png_id; /* Used to match requests with replies */ - uint16_t png_seqno; /* IN: seqno to send; OUT: seqno recieved */ + uint16_t png_seqno; /* IN: seqno to send; OUT: seqno received */ uint16_t png_datlen; /* The length of data to send in the ECHO request */ bool png_sent; /* true... the PING request has been sent */ }; @@ -164,13 +164,25 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, nllvdbg("flags: %04x\n", flags); if (pstate) { + /* Check if the network is still up + * + * REVISIT: Now does the ICMP logic know that this was the correct device? + */ + + if ((flags & NETDEV_DOWN) != 0) + { + nlldbg("ERROR: Interface is down\n"); + pstate->png_result = -ENETUNREACH; + goto end_wait; + } + /* Check if this is a ICMP ECHO reply. If so, return the sequence * number to the caller. NOTE: We may not even have sent the * requested ECHO request; this could have been the delayed ECHO * response from a previous ping. */ - if ((flags & ICMP_ECHOREPLY) != 0 && conn != NULL) + else if ((flags & ICMP_ECHOREPLY) != 0 && conn != NULL) { FAR struct icmp_iphdr_s *icmp = (FAR struct icmp_iphdr_s *)conn; @@ -332,7 +344,22 @@ int icmp_ping(in_addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, net_lock_t save; #ifdef CONFIG_NET_ARP_SEND int ret; +#endif + +#ifdef CONFIG_NETDEV_MULTINIC + FAR struct net_driver_s *dev; + /* Get the device that will be used to route this ICMP ECHO request */ + + dev = netdev_findby_ipv4addr(g_ipv4_allzeroaddr, addr); + if (dev == 0) + { + ndbg("ERROR: Not reachable\n"); + return -ENETUNREACH; + } +#endif + +#ifdef CONFIG_NET_ARP_SEND /* Make sure that the IP address mapping is in the ARP table */ ret = arp_send(addr); @@ -362,7 +389,7 @@ int icmp_ping(in_addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, state.png_cb = icmp_callback_alloc(); if (state.png_cb) { - state.png_cb->flags = (ICMP_POLL | ICMP_ECHOREPLY); + state.png_cb->flags = (ICMP_POLL | ICMP_ECHOREPLY | NETDEV_DOWN); state.png_cb->priv = (void*)&state; state.png_cb->event = ping_interrupt; state.png_result = -EINTR; /* Assume sem-wait interrupted by signal */ @@ -370,7 +397,7 @@ int icmp_ping(in_addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, /* Notify the device driver of the availability of TX data */ #ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv4_txnotify(g_ipv4_allzeroaddr, state.png_addr); + netdev_ipv4_txnotify_dev(dev); #else netdev_ipv4_txnotify(state.png_addr); #endif diff --git a/net/icmp/icmp_poll.c b/net/icmp/icmp_poll.c index 067ffef68b..9645472145 100644 --- a/net/icmp/icmp_poll.c +++ b/net/icmp/icmp_poll.c @@ -96,7 +96,7 @@ void icmp_poll(FAR struct net_driver_s *dev) /* Perform the application callback */ - (void)devif_callback_execute(dev, NULL, ICMP_POLL, g_icmp_echocallback); + (void)devif_callback_execute(dev, NULL, ICMP_POLL, dev->d_callbacks); } #endif /* CONFIG_NET && CONFIG_NET_ICMP && CONFIG_NET_ICMP_PING */ diff --git a/net/netdev/netdev.h b/net/netdev/netdev.h index 3a1c0908b2..b4e115f551 100644 --- a/net/netdev/netdev.h +++ b/net/netdev/netdev.h @@ -130,6 +130,7 @@ FAR struct net_driver_s *netdev_default(void); #ifdef CONFIG_NET_IPv4 # ifdef CONFIG_NETDEV_MULTINIC void netdev_ipv4_txnotify(in_addr_t lipaddr, in_addr_t ripaddr); +void netdev_ipv4_txnotify_dev(FAR struct net_driver_s *dev); # else void netdev_ipv4_txnotify(in_addr_t ripaddr); # endif diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c index 7d000ef09c..6a53d118ab 100644 --- a/net/netdev/netdev_ioctl.c +++ b/net/netdev/netdev_ioctl.c @@ -65,6 +65,7 @@ #include "socket/socket.h" #include "netdev/netdev.h" +#include "devif/devif.h" #include "igmp/igmp.h" #include "icmpv6/icmpv6.h" #include "route/route.h" @@ -1024,7 +1025,7 @@ void netdev_ifup(FAR struct net_driver_s *dev) void netdev_ifdown(FAR struct net_driver_s *dev) { - /* Make sure that the device supports the d_ifdown() method */ + /* Check sure that the device supports the d_ifdown() method */ if (dev->d_ifdown) { @@ -1041,6 +1042,11 @@ void netdev_ifdown(FAR struct net_driver_s *dev) dev->d_flags &= ~IFF_UP; } } + + /* Notify clients that the network has been taken down */ + + (void)devif_callback_execute(dev, NULL, NETDEV_DOWN, + dev->d_callbacks); } } diff --git a/net/netdev/netdev_register.c b/net/netdev/netdev_register.c index 18f00d6d75..ec9e880772 100644 --- a/net/netdev/netdev_register.c +++ b/net/netdev/netdev_register.c @@ -247,6 +247,10 @@ int netdev_register(FAR struct net_driver_s *dev, enum net_lltype_e lltype) devfmt = NETDEV_DEFAULT_FORMAT; #endif + /* There are no clients of the device yet */ + + dev->d_callbacks = NULL; + /* Get the next available device number and sssign a device name to * the interface */ diff --git a/net/netdev/netdev_txnotify.c b/net/netdev/netdev_txnotify.c index 9346d58a26..e188c0f827 100644 --- a/net/netdev/netdev_txnotify.c +++ b/net/netdev/netdev_txnotify.c @@ -166,4 +166,35 @@ void netdev_ipv6_txnotify(FAR const net_ipv6addr_t ripaddr) } #endif /* CONFIG_NET_IPv6 */ +/**************************************************************************** + * Function: netdev_ipv4_txnotify_dev + * + * Description: + * Notify the device driver that new TX data is available. This variant + * would be called when the upper level logic already understands how the + * packet will be routed. + * + * Parameters: + * dev - The network device driver state structure. + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user mode + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_MULTINIC +void netdev_ipv4_txnotify_dev(FAR struct net_driver_s *dev) +{ + if (dev && dev->d_txavail) + { + /* Notify the device driver that new TX data is available. */ + + (void)dev->d_txavail(dev); + } +} +#endif /* CONFIG_NET_IPv6 */ + #endif /* CONFIG_NET && CONFIG_NSOCKET_DESCRIPTORS */