From 70c59a9d912f76316c944c383512734213846cda Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 24 Oct 2017 11:23:08 -0600 Subject: [PATCH] This commit adds support for semi-standard IPPROTO_ICMP6 sockets. This is a replacement for the non-standard ICMPv6 ping support that violated the portable POSIX OS interface. Squashed commit of the following: net/icmpv6: IPPROT_ICMP6 socket logic now builds without error. net/icmpv6: Add support for read-ahead and poll(). Initial commit is just cloned from ICMP with the appropriate name changes. configs/: All defconfig filess that include CONFIG_NET_ICMPv6_SOCKET=y need to select CONFIG_SYSTEM_PING6=y and deselect CONFIG_DISABLE_POLL. Update NSH documention to show that ping6 is now a built in command. net/icmpv6: Add icmpv6_sendto.c and icmpv6_recvfrom.c. Initial versions are just clones from icmp/ with appropriate name changes. net/icmpv6: Clone some ICMP socket logic as the beginning of support for ICMPv6 socket support. Rename CONFIG_NET_ICMPv6_PING to CONFIG_NET_ICMPv6_SOCKET. Move prototype for icmpv6_ping from include/nuttx/net/icmpv6 to net/icmpv6/icmpv6.h --- Documentation/NuttShell.html | 148 ++--- TODO | 1 - configs/dk-tm4c129x/ipv6/defconfig | 4 +- configs/sama5d4-ek/ipv6/defconfig | 3 +- .../mrf24j40-starhub/defconfig | 3 +- .../samv71-xult/mrf24j40-starhub/defconfig | 4 +- configs/sim/README.txt | 2 +- configs/stm32f4discovery/ipv6/defconfig | 4 +- configs/tm4c1294-launchpad/ipv6/defconfig | 4 +- drivers/net/loopback.c | 2 +- include/netinet/in.h | 4 +- include/nuttx/net/icmpv6.h | 30 - net/devif/devif_poll.c | 8 +- net/icmp/Kconfig | 4 +- net/icmp/icmp.h | 6 +- net/icmp/icmp_input.c | 8 +- net/icmp/icmp_netpoll.c | 8 +- net/icmp/icmp_recvfrom.c | 2 +- net/icmp/icmp_sendto.c | 7 +- net/icmpv6/Kconfig | 24 +- net/icmpv6/Make.defs | 16 +- net/icmpv6/icmpv6.h | 277 ++++++++- net/icmpv6/icmpv6_conn.c | 286 +++++++++ net/icmpv6/icmpv6_input.c | 205 ++++++- net/icmpv6/icmpv6_netpoll.c | 311 ++++++++++ net/icmpv6/icmpv6_ping.c | 502 ---------------- net/icmpv6/icmpv6_poll.c | 4 +- net/icmpv6/icmpv6_recvfrom.c | 556 ++++++++++++++++++ net/icmpv6/icmpv6_sendto.c | 509 ++++++++++++++++ net/icmpv6/icmpv6_sockif.c | 520 ++++++++++++++++ net/inet/inet_sockif.c | 10 + net/net_initialize.c | 7 + 32 files changed, 2775 insertions(+), 704 deletions(-) create mode 100644 net/icmpv6/icmpv6_conn.c create mode 100644 net/icmpv6/icmpv6_netpoll.c delete mode 100644 net/icmpv6/icmpv6_ping.c create mode 100644 net/icmpv6/icmpv6_recvfrom.c create mode 100644 net/icmpv6/icmpv6_sendto.c create mode 100644 net/icmpv6/icmpv6_sockif.c diff --git a/Documentation/NuttShell.html b/Documentation/NuttShell.html index a51dd516b1..a4056e6d66 100644 --- a/Documentation/NuttShell.html +++ b/Documentation/NuttShell.html @@ -8,7 +8,7 @@

NuttShell (NSH)

-

Last Updated: October 23, 2017

+

Last Updated: October 24, 2017

@@ -353,157 +353,151 @@
- 2.45 Check Network Peer (ping6) + 2.45 Shut the system down (poweroff)
- 2.46 Shut the system down (poweroff) + 2.46 Send File Via TFTP (put)
- 2.47 Send File Via TFTP (put) + 2.47 Show Current Working Directory (pwd)
- 2.48 Show Current Working Directory (pwd) + 2.48 Show target of a link (readlink)
- 2.49 Show target of a link (readlink) + 2.49 Reset and reboot the system (reboot)
- 2.50 Reset and reboot the system (reboot) + 2.50 Remove a File (rm)
- 2.51 Remove a File (rm) + 2.51 Remove a Directory (rmdir)
- 2.52 Remove a Directory (rmdir) + 2.52 Remove on OS Module (rmmod)
- 2.53 Remove on OS Module (rmmod) + 2.53 Show routing table (route)
- 2.54 Show routing table (route) + 2.54 Set an Environment Variable (set)
- 2.55 Set an Environment Variable (set) + 2.55 Execute an NSH Script (sh)
- 2.56 Execute an NSH Script (sh) + 2.56 Shut the system down (shutdown)
- 2.57 Shut the system down (shutdown) + 2.57 Wait for Seconds (sleep)
- 2.58 Wait for Seconds (sleep) + 2.58 Start the Telnet Daemon (telnetd)
- 2.59 Start the Telnet Daemon (telnetd) + 2.59 Time execution of another command (time)
- 2.60 Time execution of another command (time) + 2.60 Unmount a File System (umount)
- 2.61 Unmount a File System (umount) + 2.61 Print system information (uname)
- 2.62 Print system information (uname) + 2.62 Unset an Environment Variable (unset)
- 2.63 Unset an Environment Variable (unset) + 2.63 URL Decode (urldecode)
- 2.64 URL Decode (urldecode) + 2.64 URL Encode (urlencode)
- 2.65 URL Encode (urlencode) + 2.65 Add a New User (useradd)
- 2.66 Add a New User (useradd) + 2.66 Delete a user (userdel)
- 2.67 Delete a user (userdel) + 2.67 Wait for Microseconds (usleep)
- 2.68 Wait for Microseconds (usleep) + 2.68 Get File Via HTTP (wget)
- 2.69 Get File Via HTTP (wget) - - - -
- - 2.70 Hexadecimal Dump of Memory (xd) + 2.69 Hexadecimal Dump of Memory (xd) @@ -515,7 +509,7 @@
- 3.1 Check Network Peer (ping) + 3.1 Check Network Peer (ping/pin6) @@ -2532,26 +2526,7 @@ passwd <username> <password> - -
-

2.45 Check Network Peer (ping6)

-
- -

Command Syntax:

- -

- Synopsis. - Test the network communication with a remote peer. -

- ping6 differs from ping in that it uses IPv6 addressing. -

- - - -
-

2.46 Shut the system down (poweroff)

+

2.45 Shut the system down (poweroff)

@@ -2573,7 +2548,7 @@ poweroff
-

2.47 Send File Via TFTP (put)

+

2.46 Send File Via TFTP (put)

@@ -2608,7 +2583,7 @@ put [-b|-n] [-f <remote-path>] -h <ip-address> <local-path>
-

2.48 Show Current Working Directory (pwd)

+

2.47 Show Current Working Directory (pwd)

@@ -2638,7 +2613,7 @@ nsh>
-

2.49 Show target of a link (readlink)

+

2.48 Show target of a link (readlink)

@@ -2655,7 +2630,7 @@ readlink <link>
-

2.50 Reboot the system (reboot)

+

2.49 Reboot the system (reboot)

@@ -2676,7 +2651,7 @@ reboot
-

2.51 Remove a File (rm)

+

2.50 Remove a File (rm)

@@ -2710,7 +2685,7 @@ nsh>
-

2.52 Remove a Directory (rmdir)

+

2.51 Remove a Directory (rmdir)

@@ -2745,7 +2720,7 @@ nsh>
-

2.53 Remove on OS Module (rmmod)

+

2.52 Remove on OS Module (rmmod)

@@ -2773,7 +2748,7 @@ nsh>
-

2.54 Show routing table (route)

+

2.53 Show routing table (route)

@@ -2793,7 +2768,7 @@ route ipv4|ipv6
-

2.55 Set an Environment Variable (set)

+

2.54 Set an Environment Variable (set)

@@ -2862,7 +2837,7 @@ nsh>
-

2.56 Execute an NSH Script (sh)

+

2.55 Execute an NSH Script (sh)

@@ -2880,7 +2855,7 @@ sh <script-path>
-

2.57 Shut the system down (shutdown)

+

2.56 Shut the system down (shutdown)

@@ -2901,7 +2876,7 @@ shutdown [--reboot]
-

2.58 Wait for Seconds (sleep)

+

2.57 Wait for Seconds (sleep)

@@ -2918,7 +2893,7 @@ sleep <sec>
-

2.59 Time Start the Telnet Daemon (telnetd)

+

2.58 Time Start the Telnet Daemon (telnetd)

@@ -2944,7 +2919,7 @@ telnetd
-

2.60 Time execution of another command (time)

+

2.59 Time execution of another command (time)

@@ -3002,7 +2977,7 @@ nsh>
-

2.61 Unmount a File System (umount)

+

2.60 Unmount a File System (umount)

@@ -3032,7 +3007,7 @@ nsh>
-

2.62 Print system information (uname)

+

2.61 Print system information (uname)

@@ -3099,7 +3074,7 @@ uname [-a | -imnoprsv]
-

2.63 Unset an Environment Variable (unset)

+

2.62 Unset an Environment Variable (unset)

@@ -3125,7 +3100,7 @@ nsh>
-

2.64 URL Decode (urldecode)

+

2.63 URL Decode (urldecode)

@@ -3142,7 +3117,7 @@ urldecode [-f] <string or filepath>
-

2.65 URL Encode (urlencode)

+

2.64 URL Encode (urlencode)

@@ -3159,7 +3134,7 @@ urlencode [-f] <string or filepath>
-

2.66 Add a New User (useradd)

+

2.65 Add a New User (useradd)

@@ -3176,7 +3151,7 @@ useradd <username> <password>
-

2.67 Delete a user (userdel)

+

2.66 Delete a user (userdel)

@@ -3193,7 +3168,7 @@ userdel <username>
-

2.68 Wait for Microseconds (usleep)

+

2.67 Wait for Microseconds (usleep)

@@ -3210,7 +3185,7 @@ usleep <usec>
-

2.69 Get File Via HTTP (wget)

+

2.68 Get File Via HTTP (wget)

@@ -3237,7 +3212,7 @@ wget [-o <local-path>] <url>
-

2.70 Hexadecimal Dump of Memory (xd)

+

2.69 Hexadecimal Dump of Memory (xd)

@@ -3285,7 +3260,7 @@ nsh>
-

3.1 Check Network Peer (ping)

+

3.1 Check Network Peer (ping/ping6)

@@ -3293,6 +3268,7 @@ nsh>

Command Syntax:

Synopsis. @@ -3314,6 +3290,9 @@ PING 10.0.0.1 56 bytes of data 10 packets transmitted, 10 received, 0% packet loss, time 10190 ms nsh> +

+ ping6 differs from ping in that it uses IPv6 addressing. +

@@ -3574,12 +3553,6 @@ nsh> - - - - - @@ -3761,6 +3734,11 @@ nsh> + + + +
!CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE && CONFIG_NSH_LOGIN_PASSWD CONFIG_NSH_DISABLE_PASSWD
ping6CONFIG_NET && CONFIG_NET_ICMPv6 && - CONFIG_NET_ICMPv6_PING && !CONFIG_DISABLE_SIGNALSCONFIG_NSH_DISABLE_PING6
poweroff CONFIG_BOARDCTL_POWEROFFCONFIG_NET && CONFIG_NET_ICMP && CONFIG_NET_ICMP_SOCKET && CONFIG_SYSTEM_PING && !CONFIG_DISABLE_SIGNALS && !CONFIG_DISABLE_POLL
ping6CONFIG_NET && CONFIG_NET_ICMPv6 && + CONFIG_NET_ICMPv6_SOCKET && CONFIG_SYSTEM_PING6 && !CONFIG_DISABLE_SIGNALS && !CONFIG_DISABLE_POLL
@@ -5556,7 +5534,7 @@ xxd -i romfs_img >nsh_romfsimg.h
  • Password File
  • Password File, ROMFS
  • ping
  • -
  • ping6
  • +
  • ping6
  • poweroff
  • Prompt
  • ps
  • diff --git a/TODO b/TODO index 4665a9d244..d75df2b1d4 100644 --- a/TODO +++ b/TODO @@ -646,7 +646,6 @@ o Kernel/Protected Build COMMAND KERNEL INTERFACE(s) -------- ---------------------------------------------- mkrd ramdisk_register() - ping6 icmpv6_ping() mount foreach_mountpoint() Status: Open diff --git a/configs/dk-tm4c129x/ipv6/defconfig b/configs/dk-tm4c129x/ipv6/defconfig index e318c48602..c7b6179b4f 100644 --- a/configs/dk-tm4c129x/ipv6/defconfig +++ b/configs/dk-tm4c129x/ipv6/defconfig @@ -16,7 +16,6 @@ CONFIG_ARCH="arm" CONFIG_ARMV7M_TOOLCHAIN_CODESOURCERYW=y CONFIG_BOARD_LOOPSPERMSEC=11401 CONFIG_BUILTIN=y -CONFIG_DISABLE_POLL=y CONFIG_EXAMPLES_NSH=y CONFIG_HOST_WINDOWS=y CONFIG_I2CTOOL_MAXBUS=6 @@ -30,7 +29,7 @@ CONFIG_NET_BROADCAST=y CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=536 CONFIG_NET_ICMPv6_NEIGHBOR=y -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -66,6 +65,7 @@ CONFIG_START_MONTH=3 CONFIG_START_YEAR=2013 CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_LM75=y +CONFIG_SYSTEM_PING6=y CONFIG_TIVA_BOARDMAC=y CONFIG_TIVA_ETHERNET=y CONFIG_TIVA_GPIOP_IRQS=y diff --git a/configs/sama5d4-ek/ipv6/defconfig b/configs/sama5d4-ek/ipv6/defconfig index efc9a68def..f3ac2b21f7 100644 --- a/configs/sama5d4-ek/ipv6/defconfig +++ b/configs/sama5d4-ek/ipv6/defconfig @@ -58,7 +58,7 @@ CONFIG_NET_BROADCAST=y CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=562 CONFIG_NET_ICMPv6_NEIGHBOR=y -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -148,6 +148,7 @@ CONFIG_SDIO_BLOCKSETUP=y CONFIG_SYMTAB_ORDEREDBYNAME=y CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_NXPLAYER=y +CONFIG_SYSTEM_PING6=y CONFIG_USART3_SERIAL_CONSOLE=y CONFIG_USBHOST_HIDKBD=y CONFIG_USBHOST_ISOC_DISABLE=y diff --git a/configs/same70-xplained/mrf24j40-starhub/defconfig b/configs/same70-xplained/mrf24j40-starhub/defconfig index 501b38fa01..58ec4415ca 100644 --- a/configs/same70-xplained/mrf24j40-starhub/defconfig +++ b/configs/same70-xplained/mrf24j40-starhub/defconfig @@ -59,7 +59,7 @@ CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=536 CONFIG_NET_HOSTNAME="MRF24J40-Hub" CONFIG_NET_ICMPv6_NEIGHBOR=y -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -123,6 +123,7 @@ CONFIG_START_DAY=16 CONFIG_START_MONTH=8 CONFIG_SYSLOG_BUFFER=y CONFIG_SYSLOG_INTBUFFER=y +CONFIG_SYSTEM_PING6=y CONFIG_SYSTEM_TELNET_CLIENT=y CONFIG_UART3_SERIAL_CONSOLE=y CONFIG_USER_ENTRYPOINT="nsh_main" diff --git a/configs/samv71-xult/mrf24j40-starhub/defconfig b/configs/samv71-xult/mrf24j40-starhub/defconfig index bbb7e5deb9..44a209cba9 100644 --- a/configs/samv71-xult/mrf24j40-starhub/defconfig +++ b/configs/samv71-xult/mrf24j40-starhub/defconfig @@ -23,7 +23,6 @@ CONFIG_AT24XX_EXTSIZE=160 CONFIG_AT24XX_SIZE=2 CONFIG_BOARD_LOOPSPERMSEC=51262 CONFIG_BUILTIN=y -CONFIG_DISABLE_POLL=y CONFIG_DRIVERS_IEEE802154=y CONFIG_DRIVERS_WIRELESS=y CONFIG_ETH0_PHY_KSZ8061=y @@ -61,7 +60,7 @@ CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=536 CONFIG_NET_HOSTNAME="MRF24J40-Hub" CONFIG_NET_ICMPv6_NEIGHBOR=y -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -130,6 +129,7 @@ CONFIG_SCHED_WAITPID=y CONFIG_SDCLONE_DISABLE=y CONFIG_SDIO_BLOCKSETUP=y CONFIG_START_MONTH=7 +CONFIG_SYSTEM_PING6=y CONFIG_UART3_SERIAL_CONSOLE=y CONFIG_USER_ENTRYPOINT="nsh_main" CONFIG_WIRELESS_IEEE802154=y diff --git a/configs/sim/README.txt b/configs/sim/README.txt index 6c826bc1d3..2d6ed00f91 100644 --- a/configs/sim/README.txt +++ b/configs/sim/README.txt @@ -550,7 +550,7 @@ ipforward +CONFIG_EXAMPLES_IPFORWARD_ICMPv6=y +CONFIG_NET_ICMPv6=y - +CONFIG_NET_ICMPv6_PING=y + +CONFIG_NET_ICMPv6_SOCKET=y +CONFIG_NET_ETHERNET=y +CONFIG_NET_IPFORWARD_BROADCAST=y diff --git a/configs/stm32f4discovery/ipv6/defconfig b/configs/stm32f4discovery/ipv6/defconfig index 32caa39982..9d81a71f6e 100644 --- a/configs/stm32f4discovery/ipv6/defconfig +++ b/configs/stm32f4discovery/ipv6/defconfig @@ -16,7 +16,6 @@ CONFIG_ARCH="arm" CONFIG_ARMV7M_TOOLCHAIN_CODESOURCERYW=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y -CONFIG_DISABLE_POLL=y CONFIG_ETH0_PHY_LAN8720=y CONFIG_EXAMPLES_NSH_CXXINITIALIZE=y CONFIG_EXAMPLES_NSH=y @@ -38,7 +37,7 @@ CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=536 CONFIG_NET_HOSTNAME="STM32F4-Discovery" CONFIG_NET_ICMPv6_NEIGHBOR=y -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -89,6 +88,7 @@ CONFIG_STM32_SDIO=y CONFIG_STM32_SPI1=y CONFIG_STM32_USART6=y CONFIG_STM32F4DISBB=y +CONFIG_SYSTEM_PING6=y CONFIG_USART6_RXBUFSIZE=64 CONFIG_USART6_SERIAL_CONSOLE=y CONFIG_USART6_TXBUFSIZE=64 diff --git a/configs/tm4c1294-launchpad/ipv6/defconfig b/configs/tm4c1294-launchpad/ipv6/defconfig index fcf6fab923..6587cc6ba7 100644 --- a/configs/tm4c1294-launchpad/ipv6/defconfig +++ b/configs/tm4c1294-launchpad/ipv6/defconfig @@ -13,7 +13,6 @@ CONFIG_ARCH_STACKDUMP=y CONFIG_ARCH="arm" CONFIG_BOARD_LOOPSPERMSEC=11401 CONFIG_BUILTIN=y -CONFIG_DISABLE_POLL=y CONFIG_EXAMPLES_NSH=y CONFIG_I2C=y CONFIG_I2CTOOL_MAXBUS=6 @@ -25,7 +24,7 @@ CONFIG_MAX_WDOGPARMS=2 CONFIG_NET_BROADCAST=y CONFIG_NET_ETH_MTU=590 CONFIG_NET_ETH_TCP_RECVWNDO=536 -CONFIG_NET_ICMPv6_PING=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_ICMPv6=y CONFIG_NET_IPv6=y CONFIG_NET_SOCKOPTS=y @@ -60,6 +59,7 @@ CONFIG_START_DAY=24 CONFIG_START_MONTH=3 CONFIG_START_YEAR=2013 CONFIG_SYSTEM_I2CTOOL=y +CONFIG_SYSTEM_PING6=y CONFIG_TIVA_BOARDMAC=y CONFIG_TIVA_ETHERNET=y CONFIG_TIVA_I2C6=y diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 8b48a45056..10e2408d09 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -204,7 +204,7 @@ static int lo_txpoll(FAR struct net_driver_s *dev) else #endif { - nwarn("WARNING: Unrecognized packet type dropped: %02x\n", IPv4BUF->vhl); + nwarn("WARNING: Unrecognized IP version\n"); NETDEV_RXDROPPED(&priv->lo_dev); priv->lo_dev.d_len = 0; } diff --git a/include/netinet/in.h b/include/netinet/in.h index c2d2244c7a..a9bc1c0689 100644 --- a/include/netinet/in.h +++ b/include/netinet/in.h @@ -51,7 +51,7 @@ /* Values for protocol argument to socket() */ -#define IPPROTO_IP 0 /* Dummy protocol for TCP */ +#define IPPROTO_IP 0 /* Default protocol */ #define IPPROTO_HOPOPTS 0 /* IPv6 Hop-by-Hop options. */ #define IPPROTO_ICMP 1 /* Internet Control Message Protocol */ #define IPPROTO_IGMP 2 /* Internet Group Management Protocol */ @@ -70,7 +70,7 @@ #define IPPROTO_GRE 47 /* General Routing Encapsulation. */ #define IPPROTO_ESP 50 /* Encapsulation Security Payload protocol */ #define IPPROTO_AH 51 /* Authentication Header protocol */ -#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_ICMP6 58 /* Internal Control Message Protocol v6 */ #define IPPROTO_NONE 59 /* IPv6 no next header. */ #define IPPROTO_DSTOPTS 60 /* IPv6 destination options. */ #define IPPROTO_MTP 92 /* Multicast Transport Protocol. */ diff --git a/include/nuttx/net/icmpv6.h b/include/nuttx/net/icmpv6.h index 9d4417980b..b840d75357 100644 --- a/include/nuttx/net/icmpv6.h +++ b/include/nuttx/net/icmpv6.h @@ -363,36 +363,6 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -/**************************************************************************** - * Name: imcp_ping - * - * Description: - * Send a ECHO request and wait for the ECHO response - * - * Parameters: - * addr - The IP address of the peer to send the ICMP ECHO request to - * in network order. - * id - The ID to use in the ICMP ECHO request. This number should be - * unique; only ECHO responses with this matching ID will be - * processed (host order) - * seqno - The sequence number used in the ICMP ECHO request. NOT used - * to match responses (host order) - * dsecs - Wait up to this many deci-seconds for the ECHO response to be - * returned (host order). - * - * Return: - * seqno of received ICMP ECHO with matching ID (may be different - * from the seqno argument (may be a delayed response from an earlier - * ping with the same ID). Or a negated errno on any failure. - * - * Assumptions: - * Called from the user level with interrupts enabled. - * - ****************************************************************************/ - -int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, - uint16_t datalen, int dsecs); - #undef EXTERN #ifdef __cplusplus } diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index 1ebddbb6f2..5a3f555635 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -308,7 +308,7 @@ static inline int devif_poll_icmp(FAR struct net_driver_s *dev, * ****************************************************************************/ -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#if defined(CONFIG_NET_ICMPv6_SOCKET) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) static inline int devif_poll_icmpv6(FAR struct net_driver_s *dev, devif_poll_callback_t callback) { @@ -324,7 +324,7 @@ static inline int devif_poll_icmpv6(FAR struct net_driver_s *dev, return callback(dev); } -#endif /* CONFIG_NET_ICMPv6_PING || CONFIG_NET_ICMPv6_NEIGHBOR*/ +#endif /* CONFIG_NET_ICMPv6_SOCKET || CONFIG_NET_ICMPv6_NEIGHBOR*/ /**************************************************************************** * Name: devif_poll_forward @@ -351,7 +351,7 @@ static inline int devif_poll_forward(FAR struct net_driver_s *dev, return callback(dev); } -#endif /* CONFIG_NET_ICMPv6_PING || CONFIG_NET_ICMPv6_NEIGHBOR*/ +#endif /* CONFIG_NET_ICMPv6_SOCKET || CONFIG_NET_ICMPv6_NEIGHBOR*/ /**************************************************************************** * Name: devif_poll_igmp @@ -610,7 +610,7 @@ int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) if (!bstop) #endif -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#if defined(CONFIG_NET_ICMPv6_SOCKET) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) { /* Traverse all of the tasks waiting to send an ICMPv6 ECHO request. */ diff --git a/net/icmp/Kconfig b/net/icmp/Kconfig index 7ed2ecd05b..9e541f7f5f 100644 --- a/net/icmp/Kconfig +++ b/net/icmp/Kconfig @@ -20,8 +20,8 @@ config NET_ICMP_SOCKET default n ---help--- Enable support for IPPROTO_ICMP sockets. These sockets are needed - for application level support application for sending ECHO (ping) - requests and associated ECHO replies. + for application level support for sending ECHO (ping) requests and + receiving associated ECHO replies. if NET_ICMP_SOCKET diff --git a/net/icmp/icmp.h b/net/icmp/icmp.h index a7c734caa8..ebb2a23d9a 100644 --- a/net/icmp/icmp.h +++ b/net/icmp/icmp.h @@ -321,10 +321,10 @@ ssize_t icmp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, * Name: icmp_pollsetup * * Description: - * Setup to monitor events on one UDP/IP socket + * Setup to monitor events on one ICMP socket * * Input Parameters: - * psock - The UDP/IP socket of interest + * psock - The IPPROTO_ICMP socket of interest * fds - The structure describing the events to be monitored, OR NULL if * this is a request to stop monitoring events. * @@ -341,7 +341,7 @@ int icmp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds); * Name: icmp_pollteardown * * Description: - * Teardown monitoring of events on an UDP/IP socket + * Teardown monitoring of events on an ICMP socket * * Input Parameters: * psock - The IPPROTO_ICMP socket of interest diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index 0d993504dd..8e9c289a49 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -72,7 +72,7 @@ #define ICMPSIZE ((dev)->d_len - IPv4_HDRLEN) /**************************************************************************** - * Public Functions + * Private Functions ****************************************************************************/ /**************************************************************************** @@ -95,8 +95,8 @@ ****************************************************************************/ #ifdef CONFIG_NET_ICMP_SOCKET -uint16_t icmp_datahandler(FAR struct net_driver_s *dev, - FAR struct icmp_conn_s *conn) +static uint16_t icmp_datahandler(FAR struct net_driver_s *dev, + FAR struct icmp_conn_s *conn) { FAR struct ipv4_hdr_s *ipv4; struct sockaddr_in inaddr; @@ -123,7 +123,7 @@ uint16_t icmp_datahandler(FAR struct net_driver_s *dev, ipv4 = IPv4BUF; inaddr.sin_family = AF_INET; - inaddr.sin_port = INADDR_ANY; + inaddr.sin_port = 0; net_ipv4addr_copy(inaddr.sin_addr.s_addr, net_ip4addr_conv32(ipv4->srcipaddr)); diff --git a/net/icmp/icmp_netpoll.c b/net/icmp/icmp_netpoll.c index 07eb229c8a..aad9f8f33d 100644 --- a/net/icmp/icmp_netpoll.c +++ b/net/icmp/icmp_netpoll.c @@ -116,7 +116,7 @@ static uint16_t icmp_poll_eventhandler(FAR struct net_driver_s *dev, /* ICMP_POLL is a sign that we are free to send data. */ - if ((flags & ICMP_POLL) != 0) + if ((flags & DEVPOLL_MASK) == ICMP_POLL) { eventset |= (POLLOUT & info->fds->events); } @@ -148,10 +148,10 @@ static uint16_t icmp_poll_eventhandler(FAR struct net_driver_s *dev, * Name: icmp_pollsetup * * Description: - * Setup to monitor events on one UDP/IP socket + * Setup to monitor events on one ICMP socket * * Input Parameters: - * psock - The UDP/IP socket of interest + * psock - The IPPROTO_ICMP socket of interest * fds - The structure describing the events to be monitored, OR NULL if * this is a request to stop monitoring events. * @@ -261,7 +261,7 @@ errout_with_lock: * Name: icmp_pollteardown * * Description: - * Teardown monitoring of events on an UDP/IP socket + * Teardown monitoring of events on an ICMP socket * * Input Parameters: * psock - The IPPROTO_ICMP socket of interest diff --git a/net/icmp/icmp_recvfrom.c b/net/icmp/icmp_recvfrom.c index 0559f37cf4..b9cccd546f 100644 --- a/net/icmp/icmp_recvfrom.c +++ b/net/icmp/icmp_recvfrom.c @@ -525,7 +525,7 @@ ssize_t icmp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, { inaddr = (FAR struct sockaddr_in *)from; inaddr->sin_family = AF_INET; - inaddr->sin_port = INADDR_ANY; + inaddr->sin_port = 0; net_ipv4addr_copy(inaddr->sin_addr.s_addr, state.recv_from); } diff --git a/net/icmp/icmp_sendto.c b/net/icmp/icmp_sendto.c index 951d7e6666..9ee74e000e 100644 --- a/net/icmp/icmp_sendto.c +++ b/net/icmp/icmp_sendto.c @@ -75,9 +75,10 @@ * Pre-processor Definitions ****************************************************************************/ -#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define ICMPBUF ((struct icmp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) -#define ICMPDAT (&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN + ICMP_DHRLEN]) +#define IPv4BUF \ + ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPBUF \ + ((struct icmp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) /**************************************************************************** * Private Types diff --git a/net/icmpv6/Kconfig b/net/icmpv6/Kconfig index 62f4ca8943..f78793e8b1 100644 --- a/net/icmpv6/Kconfig +++ b/net/icmpv6/Kconfig @@ -16,17 +16,13 @@ config NET_ICMPv6 if NET_ICMPv6 -config NET_ICMPv6_PING - bool "ICMPv6 ping interfaces" +config NET_ICMPv6_SOCKET + bool "IPPROTO_ICMP6 socket support" default n - depends on BUILD_FLAT ---help--- - Provide interfaces to support application level support for - for sending ECHO (ping) requests and associating ECHO replies. - - NOTE: Calling these interfaces from application space is a - violation of the OS/application interface but for historical - reasons, is permitted in the flat build. + Enable support for IPPROTO_ICMP6 sockets. These sockets are needed + for application level support for sending ICMPv7 ECHO requests and + receiving associated ICMPv6 ECHO replies. config NET_ICMPv6_NEIGHBOR bool "Solicit destination addresses" @@ -183,6 +179,16 @@ config NET_ICMPv6_PREFIX_8 default for all eight values is fc00::0. endif # NET_ICMPv6_ROUTER + +if NET_ICMPv6_SOCKET + +config NET_ICMPv6_NCONNS + int "Max ICMPv6 packet sockets" + default 4 + depends on MM_IOB + +endif # NET_ICMPv6_SOCKET + endif # NET_ICMPv6 endmenu # ICMPv6 Networking Support endif # NET_IPv6 diff --git a/net/icmpv6/Make.defs b/net/icmpv6/Make.defs index 6c9b70aee7..b25acbe350 100644 --- a/net/icmpv6/Make.defs +++ b/net/icmpv6/Make.defs @@ -39,20 +39,22 @@ ifeq ($(CONFIG_NET_ICMPv6),y) NET_CSRCS += icmpv6_input.c icmpv6_solicit.c icmpv6_advertise.c -ifeq ($(CONFIG_NET_ICMPv6_PING),y) -NET_CSRCS += icmpv6_ping.c +ifeq ($(CONFIG_NET_ICMPv6_SOCKET),y) +SOCK_CSRCS += icmpv6_sockif.c icmpv6_conn.c icmpv6_sendto.c +SOCK_CSRCS += icmpv6_recvfrom.c +ifeq ($(CONFIG_MM_IOB),y) +SOCK_CSRCS += icmpv6_netpoll.c +endif endif ifeq ($(CONFIG_NET_ICMPv6_NEIGHBOR),y) NET_CSRCS += icmpv6_neighbor.c icmpv6_notify.c endif -ifeq ($(CONFIG_NET_ICMPv6_PING),y) +ifeq ($(CONFIG_NET_ICMPv6_SOCKET),y) +SOCK_CSRCS += icmpv6_poll.c +else ifeq ($(CONFIG_NET_ICMPv6_NEIGHBOR),y) NET_CSRCS += icmpv6_poll.c -else -ifeq ($(CONFIG_NET_ICMPv6_NEIGHBOR),y) -NET_CSRCS += icmpv6_poll.c -endif endif ifeq ($(CONFIG_NET_ICMPv6_AUTOCONF),y) diff --git a/net/icmpv6/icmpv6.h b/net/icmpv6/icmpv6.h index 2b2c2380db..01859607a3 100644 --- a/net/icmpv6/icmpv6.h +++ b/net/icmpv6/icmpv6.h @@ -1,7 +1,7 @@ /**************************************************************************** * net/icmpv6/icmpv6.h * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -43,9 +43,14 @@ #include #include +#include #include +#include +#include +#include #include +#include #ifdef CONFIG_NET_ICMPv6 @@ -64,15 +69,34 @@ * Public Type Definitions ****************************************************************************/ -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) || \ - defined(CONFIG_NET_ICMPv6_AUTOCONF) -/* For symmetry with other protocols, a "connection" structure is - * provided. But, in this case, it is a singleton. - */ +#ifdef CONFIG_NET_ICMPv6_SOCKET +/* Representation of a IPPROTO_ICMP socket connection */ + +struct devif_callback_s; /* Forward reference */ struct icmpv6_conn_s { - FAR struct devif_callback_s *list; /* Neighbor discovery callbacks */ + dq_entry_t node; /* Supports a double linked list */ + uint16_t id; /* ICMPv6 ECHO request ID */ + uint8_t nreqs; /* Number of requests with no response received */ + uint8_t crefs; /* Reference counts on this instance */ + + /* The device that the ICMPv6 request was sent on */ + + FAR struct net_driver_s *dev; /* Needed to free the callback structure */ + +#ifdef CONFIG_MM_IOB + /* ICMPv6 response read-ahead list. A singly linked list of type struct + * iob_qentry_s where the ICMPv6 read-ahead data for the current ID is + * retained. + */ + + struct iob_queue_s readahead; /* Read-ahead buffering */ +#endif + + /* Defines the list of IPPROTO_ICMP callbacks */ + + struct devif_callback_s *list; }; #endif @@ -112,12 +136,21 @@ extern "C" # define EXTERN extern #endif +#ifdef CONFIG_NET_ICMPv6_SOCKET +/* PF_INET6 socket address family, IPPROTO_ICMP6 protocol interface */ + +EXTERN const struct sock_intf_s g_icmpv6_sockif; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ struct timespec; /* Forward reference */ struct net_driver_s; /* Forward reference */ +struct socket; /* Forward reference */ +struct sockaddr; /* Forward reference */ +struct pollfd; /* Forward reference */ /**************************************************************************** * Name: icmpv6_input @@ -180,7 +213,7 @@ int icmpv6_neighbor(const net_ipv6addr_t ipaddr); * Name: icmpv6_poll * * Description: - * Poll a UDP "connection" structure for availability of TX data + * Poll a UDP "connection" structure for availability of ICMPv6 TX data * * Parameters: * dev - The device driver structure to use in the send operation @@ -193,7 +226,7 @@ int icmpv6_neighbor(const net_ipv6addr_t ipaddr); * ****************************************************************************/ -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#if defined(CONFIG_NET_ICMPv6_SOCKET) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) void icmpv6_poll(FAR struct net_driver_s *dev); #endif @@ -475,6 +508,232 @@ void icmpv6_rnotify(FAR struct net_driver_s *dev, const net_ipv6addr_t draddr, # define icmpv6_rnotify(d,p,l) #endif +/**************************************************************************** + * Name: imcp_ping + * + * Description: + * Send a ECHO request and wait for the ECHO response + * + * Parameters: + * addr - The IP address of the peer to send the ICMPv6 ECHO request to + * in network order. + * id - The ID to use in the ICMPv6 ECHO request. This number should be + * unique; only ECHO responses with this matching ID will be + * processed (host order) + * seqno - The sequence number used in the ICMPv6 ECHO request. NOT used + * to match responses (host order) + * dsecs - Wait up to this many deci-seconds for the ECHO response to be + * returned (host order). + * + * Return: + * seqno of received ICMPv6 ECHO with matching ID (may be different + * from the seqno argument (may be a delayed response from an earlier + * ping with the same ID). Or a negated errno on any failure. + * + * Assumptions: + * Called from the user level with interrupts enabled. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, + uint16_t datalen, int dsecs); +#endif + +/**************************************************************************** + * Name: icmpv6_sock_initialize + * + * Description: + * Initialize the IPPROTO_ICMP socket connection structures. Called once + * and only from the network initialization layer. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +void icmpv6_sock_initialize(void); +#endif + +/**************************************************************************** + * Name: icmpv6_alloc + * + * Description: + * Allocate a new, uninitialized IPPROTO_ICMP socket connection structure. + * This is normally something done by the implementation of the socket() + * interface. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +FAR struct icmpv6_conn_s *icmpv6_alloc(void); +#endif + +/**************************************************************************** + * Name: icmpv6_free + * + * Description: + * Free a IPPROTO_ICMP socket connection structure that is no longer in + * use. This should be done by the implementation of close(). + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +void icmpv6_free(FAR struct icmpv6_conn_s *conn); +#endif + +/**************************************************************************** + * Name: icmpv6_active() + * + * Description: + * Find a connection structure that is the appropriate connection to be + * used with the provided ECHO request ID. + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +FAR struct icmpv6_conn_s *icmpv6_active(uint16_t id); +#endif + +/**************************************************************************** + * Name: icmpv6_nextconn + * + * Description: + * Traverse the list of allocated packet connections + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn); +#endif + +/**************************************************************************** + * Name: icmpv6_findconn + * + * Description: + * Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO response + * with this ID from this device + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev, + uint8_t id); +#endif + +/**************************************************************************** + * Name: icmpv6_sendto + * + * Description: + * Implements the sendto() operation for the case of the IPPROTO_ICMP6 + * socket. The 'buf' parameter points to a block of memory that includes + * an ICMPv6 request header, followed by any payload that accompanies the + * request. The 'len' parameter includes both the size of the ICMPv6 + * header and the following payload. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send_to() for the list of appropriate error + * values. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +ssize_t icmpv6_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, + int flags, FAR const struct sockaddr *to, socklen_t tolen); +#endif + +/**************************************************************************** + * Name: icmpv6_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET6 + * data gram socket with the IPPROTO_ICMP6 protocol. icmpv6_recvfrom() + * receives ICMPv6 ECHO replies for the a socket. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +ssize_t icmpv6_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); +#endif + +/**************************************************************************** + * Name: icmpv6_pollsetup + * + * Description: + * Setup to monitor events on one ICMPv6 socket + * + * Input Parameters: + * psock - The IPPROTO_ICMP6 socket of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +int icmpv6_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds); +#endif + +/**************************************************************************** + * Name: icmpv6_pollteardown + * + * Description: + * Teardown monitoring of events on an ICMPv6 socket + * + * Input Parameters: + * psock - The IPPROTO_ICMP6 socket of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +int icmpv6_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/net/icmpv6/icmpv6_conn.c b/net/icmpv6/icmpv6_conn.c new file mode 100644 index 0000000000..614285f862 --- /dev/null +++ b/net/icmpv6/icmpv6_conn.c @@ -0,0 +1,286 @@ +/**************************************************************************** + * net/icmp/icmpv6_conn.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "devif/devif.h" +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_NET_ICMPv6_SOCKET + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The array containing all IPPROTO_ICMP socket connections */ + +static struct icmpv6_conn_s g_icmpv6_connections[CONFIG_NET_ICMPv6_NCONNS]; + +/* A list of all free IPPROTO_ICMP socket connections */ + +static dq_queue_t g_free_icmpv6_connections; +static sem_t g_free_sem; + +/* A list of all allocated IPPROTO_ICMP socket connections */ + +static dq_queue_t g_active_icmpv6_connections; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_sock_initialize + * + * Description: + * Initialize the IPPROTO_ICMP socket connection structures. Called once + * and only from the network initialization layer. + * + ****************************************************************************/ + +void icmpv6_sock_initialize(void) +{ + int i; + + /* Initialize the queues */ + + dq_init(&g_free_icmpv6_connections); + dq_init(&g_active_icmpv6_connections); + nxsem_init(&g_free_sem, 0, 1); + + for (i = 0; i < CONFIG_NET_ICMPv6_NCONNS; i++) + { + /* Move the connection structure to the free list */ + + dq_addlast(&g_icmpv6_connections[i].node, &g_free_icmpv6_connections); + } +} + +/**************************************************************************** + * Name: icmpv6_alloc + * + * Description: + * Allocate a new, uninitialized IPPROTO_ICMP socket connection structure. + * This is normally something done by the implementation of the socket() + * interface. + * + ****************************************************************************/ + +FAR struct icmpv6_conn_s *icmpv6_alloc(void) +{ + FAR struct icmpv6_conn_s *conn = NULL; + int ret; + + /* The free list is only accessed from user, non-interrupt level and + * is protected by a semaphore (that behaves like a mutex). + */ + + ret = net_lockedwait(&g_free_sem); + if (ret >= 0) + { + conn = (FAR struct icmpv6_conn_s *)dq_remfirst(&g_free_icmpv6_connections); + if (conn != NULL) + { + /* Clear the connection structure */ + + memset(conn, 0, sizeof(struct icmpv6_conn_s)); + + /* Enqueue the connection into the active list */ + + dq_addlast(&conn->node, &g_active_icmpv6_connections); + } + + nxsem_post(&g_free_sem); + } + + return conn; +} + +/**************************************************************************** + * Name: icmpv6_free + * + * Description: + * Free a IPPROTO_ICMP socket connection structure that is no longer in + * use. This should be done by the implementation of close(). + * + ****************************************************************************/ + +void icmpv6_free(FAR struct icmpv6_conn_s *conn) +{ + int ret; + + /* The free list is only accessed from user, non-interrupt level and + * is protected by a semaphore (that behaves like a mutex). + */ + + DEBUGASSERT(conn->crefs == 0); + + /* Take the semaphore (perhaps waiting) */ + + while ((ret = net_lockedwait(&g_free_sem)) < 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + DEBUGASSERT(ret == -EINTR); + } + + UNUSED(ret); + + /* Is this the last reference on the connection? It might not be if the + * socket was cloned. + */ + + if (conn->crefs > 1) + { + /* No.. just decrement the reference count */ + + conn->crefs--; + } + else + { + /* Remove the connection from the active list */ + + dq_rem(&conn->node, &g_active_icmpv6_connections); + + /* Free the connection */ + + dq_addlast(&conn->node, &g_free_icmpv6_connections); + nxsem_post(&g_free_sem); + } +} + +/**************************************************************************** + * Name: icmpv6_active() + * + * Description: + * Find a connection structure that is the appropriate connection to be + * used with the provided ECHO request ID. + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +FAR struct icmpv6_conn_s *icmpv6_active(uint16_t id) +{ + FAR struct icmpv6_conn_s *conn = + (FAR struct icmpv6_conn_s *)g_active_icmpv6_connections.head; + + while (conn != NULL) + { + /* FIXME lmac in conn should have been set by icmpv6_bind() */ + + if (id == conn->id) + { + /* Matching connection found.. return a reference to it */ + + break; + } + + /* Look at the next active connection */ + + conn = (FAR struct icmpv6_conn_s *)conn->node.flink; + } + + return conn; +} + +/**************************************************************************** + * Name: icmpv6_nextconn + * + * Description: + * Traverse the list of allocated packet connections + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn) +{ + if (conn == NULL) + { + return (FAR struct icmpv6_conn_s *)g_active_icmpv6_connections.head; + } + else + { + return (FAR struct icmpv6_conn_s *)conn->node.flink; + } +} + +/**************************************************************************** + * Name: icmpv6_findconn + * + * Description: + * Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO response + * with this ID from this device + * + * Assumptions: + * This function is called from network logic at with the network locked. + * + ****************************************************************************/ + +FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev, uint8_t id) +{ + FAR struct icmpv6_conn_s *conn; + + for (conn = icmpv6_nextconn(NULL); conn != NULL; conn = icmpv6_nextconn(conn)) + { + if (conn->id == id && conn->dev == dev && conn->nreqs > 0) + { + return conn; + } + } + + return conn; +} +#endif /* CONFIG_NET_ICMP */ diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index cc2af0533a..5fe32d6859 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -64,8 +64,16 @@ * Pre-processor Definitions ****************************************************************************/ -#define ETHBUF ((struct eth_hdr_s *)&dev->d_buf[0]) -#define ICMPv6BUF ((struct icmpv6_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ETHBUF \ + ((struct eth_hdr_s *)&dev->d_buf[0]) +#define IPv6BUF \ + ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define IPICMPv6 \ + ((struct icmpv6_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPv6REPLY \ + ((FAR struct icmpv6_echo_reply_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +#define ICMPv6SIZE \ + ((dev)->d_len - IPv6_HDRLEN) #define ICMPv6SOLICIT \ ((struct icmpv6_neighbor_solicit_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) @@ -74,6 +82,127 @@ #define ICMPv6RADVERTISE \ ((struct icmpv6_router_advertise_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_datahandler + * + * Description: + * Handle ICMPv6 echo replies that are not accepted by the application. + * + * Input Parameters: + * dev - Device instance only the input packet in d_buf, length = d_len; + * conn - A pointer to the ICMPv6 connection structure + * buffer - A pointer to the buffer to be copied to the read-ahead + * buffers + * buflen - The number of bytes to copy to the read-ahead buffer. + * + * Returned value: + * The number of bytes actually buffered is returned. This will be either + * zero or equal to buflen; partial packets are not buffered. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_SOCKET +static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, + FAR struct icmpv6_conn_s *conn) +{ + FAR struct ipv6_hdr_s *ipv6; + struct sockaddr_in6 inaddr; + FAR struct iob_s *iob; + uint16_t offset; + uint16_t buflen; + uint8_t addrsize; + int ret; + + /* Try to allocate on I/O buffer to start the chain without waiting (and + * throttling as necessary). If we would have to wait, then drop the + * packet. + */ + + iob = iob_tryalloc(true); + if (iob == NULL) + { + nerr("ERROR: Failed to create new I/O buffer chain\n"); + return 0; + } + + /* Put the IPv6 address at the beginning of the read-ahead buffer */ + + ipv6 = IPv6BUF; + inaddr.sin6_family = AF_INET6; + inaddr.sin6_port = 0; + net_ipv6addr_copy(inaddr.sin6_addr.s6_addr16, ipv6->srcipaddr); + + /* Copy the src address info into the I/O buffer chain. We will not wait + * for an I/O buffer to become available in this context. It there is + * any failure to allocated, the entire I/O buffer chain will be discarded. + */ + + addrsize = sizeof(struct sockaddr_in6); + ret = iob_trycopyin(iob, &addrsize, sizeof(uint8_t), 0, true); + if (ret < 0) + { + /* On a failure, iob_trycopyin return a negated error value but does + * not free any I/O buffers. + */ + + nerr("ERROR: Failed to length to the I/O buffer chain: %d\n", ret); + (void)iob_free_chain(iob); + return 0; + } + + offset = sizeof(uint8_t); + + ret = iob_trycopyin(iob, (FAR const uint8_t *)&inaddr, + sizeof(struct sockaddr_in6), offset, true); + if (ret < 0) + { + /* On a failure, iob_trycopyin return a negated error value but does + * not free any I/O buffers. + */ + + nerr("ERROR: Failed to source address to the I/O buffer chain: %d\n", ret); + (void)iob_free_chain(iob); + return 0; + } + + offset += sizeof(struct sockaddr_in6); + + /* Copy the new ICMPv6 reply into the I/O buffer chain (without waiting) */ + + buflen = ICMPv6SIZE; + ret = iob_trycopyin(iob, (FAR uint8_t *)ICMPv6REPLY, buflen, offset, true); + if (ret < 0) + { + /* On a failure, iob_copyin return a negated error value but does + * not free any I/O buffers. + */ + + nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); + (void)iob_free_chain(iob); + return 0; + } + + /* Add the new I/O buffer chain to the tail of the read-ahead queue (again + * without waiting). + */ + + ret = iob_tryadd_queue(iob, &conn->readahead); + if (ret < 0) + { + nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); + (void)iob_free_chain(iob); + return 0; + } + + ninfo("Buffered %d bytes\n", buflen + addrsize + 1); + return buflen; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -98,7 +227,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) { - FAR struct icmpv6_iphdr_s *icmp = ICMPv6BUF; + FAR struct icmpv6_iphdr_s *ipicmp = IPICMPv6; #ifdef CONFIG_NET_STATISTICS g_netstats.icmpv6.recv++; @@ -106,7 +235,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) /* Handle the ICMPv6 message by its type */ - switch (icmp->type) + switch (ipicmp->type) { /* If we get a neighbor solicitation for our address we should send * a neighbor advertisement message back. @@ -125,7 +254,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) * solicitation came from. */ - icmpv6_advertise(dev, icmp->srcipaddr); + icmpv6_advertise(dev, ipicmp->srcipaddr); /* All statistics have been updated. Nothing to do but exit. */ @@ -153,7 +282,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) */ adv = ICMPv6ADVERTISE; - if (net_ipv6addr_cmp(icmp->destipaddr, dev->d_ipv6addr)) + if (net_ipv6addr_cmp(ipicmp->destipaddr, dev->d_ipv6addr)) { /* This message is required to support the Target link-layer * address option. @@ -163,12 +292,12 @@ void icmpv6_input(FAR struct net_driver_s *dev) { /* Save the sender's address mapping in our Neighbor Table. */ - neighbor_add(dev, icmp->srcipaddr, adv->tgtlladdr); + neighbor_add(dev, ipicmp->srcipaddr, adv->tgtlladdr); #ifdef CONFIG_NET_ICMPv6_NEIGHBOR /* Then notify any logic waiting for the Neighbor Advertisement */ - icmpv6_notify(icmp->srcipaddr); + icmpv6_notify(ipicmp->srcipaddr); #endif /* We consumed the packet but we don't send anything in @@ -213,7 +342,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) /* Get the length of the option data */ - pktlen = (uint16_t)icmp->len[0] << 8 | icmp->len[1]; + pktlen = (uint16_t)ipicmp->len[0] << 8 | ipicmp->len[1]; if (pktlen <= ICMPv6_RADV_MINLEN) { /* Too small to contain any options */ @@ -238,7 +367,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) if (sllopt->opttype == 1 && sllopt->optlen == 1) { - neighbor_add(dev, icmp->srcipaddr, sllopt->srclladdr); + neighbor_add(dev, ipicmp->srcipaddr, sllopt->srclladdr); } FAR struct icmpv6_prefixinfo_s *opt = @@ -254,7 +383,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) { /* Yes.. Notify any waiting threads */ - icmpv6_rnotify(dev, icmp->srcipaddr, opt->prefix, opt->preflen); + icmpv6_rnotify(dev, ipicmp->srcipaddr, opt->prefix, opt->preflen); goto icmpv_send_nothing; } @@ -277,17 +406,17 @@ void icmpv6_input(FAR struct net_driver_s *dev) * ICMPv6 checksum before we return the packet. */ - icmp->type = ICMPv6_ECHO_REPLY; + ipicmp->type = ICMPv6_ECHO_REPLY; - net_ipv6addr_copy(icmp->destipaddr, icmp->srcipaddr); - net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + net_ipv6addr_copy(ipicmp->destipaddr, ipicmp->srcipaddr); + net_ipv6addr_copy(ipicmp->srcipaddr, dev->d_ipv6addr); - icmp->chksum = 0; - icmp->chksum = ~icmpv6_chksum(dev); + ipicmp->chksum = 0; + ipicmp->chksum = ~icmpv6_chksum(dev); } break; -#ifdef CONFIG_NET_ICMPv6_PING +#ifdef CONFIG_NET_ICMPv6_SOCKET /* If an ICMPv6 echo reply is received then there should also be * a thread waiting to received the echo response. */ @@ -298,15 +427,43 @@ void icmpv6_input(FAR struct net_driver_s *dev) /* Dispatch the ECHO reply to the waiting thread */ - flags = devif_conn_event(dev, icmp, flags, dev->d_conncb); + flags = devif_conn_event(dev, ipicmp, flags, dev->d_conncb); - /* If the ECHO reply was not handled, then drop the packet */ + /* Wwas the ECHO reply consumed by any waiting thread? */ - if (flags == ICMPv6_ECHOREPLY) + if ((flags & ICMPv6_ECHOREPLY) != 0) { - /* The ECHO reply was not handled */ + FAR struct icmpv6_echo_reply_s *reply; + FAR struct icmpv6_conn_s *conn; + uint16_t nbuffered; - goto icmpv6_drop_packet; + /* Nothing consumed the ICMP reply. That might because this is + * an old, invalid reply or simply because the ping application + * has not yet put its poll or recv in place. + */ + + /* Is there any connection that might expect this reply? */ + + reply = ICMPv6REPLY; + conn = icmpv6_findconn(dev, reply->id); + if (conn == NULL) + { + /* No.. drop the packet */ + + goto icmpv6_drop_packet; + } + + /* Yes.. Add the ICMP echo reply to the IPPROTO_ICMP socket read + * ahead buffer. + */ + + nbuffered = icmpv6_datahandler(dev, conn); + if (nbuffered == 0) + { + /* Could not buffer the data.. drop the packet */ + + goto icmpv6_drop_packet; + } } } break; @@ -314,13 +471,13 @@ void icmpv6_input(FAR struct net_driver_s *dev) default: { - nwarn("WARNING: Unknown ICMPv6 type: %d\n", icmp->type); + nwarn("WARNING: Unknown ICMPv6 type: %d\n", ipicmp->type); goto icmpv6_type_error; } } ninfo("Outgoing ICMPv6 packet length: %d (%d)\n", - dev->d_len, (icmp->len[0] << 8) | icmp->len[1]); + dev->d_len, (ipicmp->len[0] << 8) | ipicmp->len[1]); #ifdef CONFIG_NET_STATISTICS g_netstats.icmpv6.sent++; diff --git a/net/icmpv6/icmpv6_netpoll.c b/net/icmpv6/icmpv6_netpoll.c new file mode 100644 index 0000000000..e646bbf418 --- /dev/null +++ b/net/icmpv6/icmpv6_netpoll.c @@ -0,0 +1,311 @@ +/**************************************************************************** + * net/icmpv6/icmpv6_netpoll.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_MM_IOB + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This is an allocated container that holds the poll-related information */ + +struct icmpv6_poll_s +{ + struct pollfd *fds; /* Needed to handle poll events */ + FAR struct devif_callback_s *cb; /* Needed to teardown the poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_poll_eventhandler + * + * Description: + * This function is called to perform the actual UDP receive operation + * via the device interface layer. + * + * Parameters: + * dev The structure of the network driver that caused the event + * conn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * This function must be called with the network locked. + * + ****************************************************************************/ + +static uint16_t icmpv6_poll_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct icmpv6_poll_s *info = (FAR struct icmpv6_poll_s *)pvpriv; + FAR struct icmpv6_conn_s *conn = (FAR struct icmpv6_conn_s *)pvconn; + pollevent_t eventset; + + ninfo("flags: %04x\n", flags); + + DEBUGASSERT(info == NULL || (info->fds != NULL && conn != NULL)); + + /* 'priv' might be null in some race conditions (?). Only process the + * the event if this poll is from the same device that the request was + * sent out on. + */ + + if (info != NULL && dev == conn->dev) + { + /* Check for data or connection availability events. */ + + eventset = 0; + if ((flags & ICMPv6_ECHOREPLY) != 0) + { + eventset |= (POLLIN & info->fds->events); + } + + /* ICMP_POLL is a sign that we are free to send data. */ + + if ((flags & DEVPOLL_MASK) == ICMPv6_POLL) + { + eventset |= (POLLOUT & info->fds->events); + } + + /* Check for loss of connection events. */ + + if ((flags & NETDEV_DOWN) != 0) + { + eventset |= ((POLLHUP | POLLERR) & info->fds->events); + } + + /* Awaken the caller of poll() is requested event occurred. */ + + if (eventset) + { + info->fds->revents |= eventset; + nxsem_post(info->fds->sem); + } + } + + return flags; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_pollsetup + * + * Description: + * Setup to monitor events on one ICMP socket + * + * Input Parameters: + * psock - The IPPROTO_ICMP socket of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +int icmpv6_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) +{ + FAR struct icmpv6_conn_s *conn = psock->s_conn; + FAR struct icmpv6_poll_s *info; + FAR struct devif_callback_s *cb; + int ret; + + DEBUGASSERT(conn != NULL && fds != NULL && conn->dev != NULL); + + /* Allocate a container to hold the poll information */ + + info = (FAR struct icmpv6_poll_s *)kmm_malloc(sizeof(struct icmpv6_poll_s)); + if (!info) + { + return -ENOMEM; + } + + /* Some of the following must be atomic */ + + net_lock(); + + /* Get the device that will provide the provide the NETDEV_DOWN event. + * NOTE: in the event that the local socket is bound to IN6ADDR_ANY, the + * dev value will be zero and there will be no NETDEV_DOWN notifications. + */ + + /* Allocate a ICMP callback structure */ + + cb = icmpv6_callback_alloc(conn->dev); + if (cb == NULL) + { + ret = -EBUSY; + goto errout_with_lock; + } + + /* Initialize the poll info container */ + + info->fds = fds; + info->cb = cb; + + /* Initialize the callback structure. Save the reference to the info + * structure as callback private data so that it will be available during + * callback processing. + */ + + cb->flags = 0; + cb->priv = (FAR void *)info; + cb->event = icmpv6_poll_eventhandler; + + if ((info->fds->events & POLLOUT) != 0) + { + cb->flags |= UDP_POLL; + } + + if ((info->fds->events & POLLIN) != 0) + { + cb->flags |= UDP_NEWDATA; + } + + if ((info->fds->events & (POLLHUP | POLLERR)) != 0) + { + cb->flags |= NETDEV_DOWN; + } + + /* Save the reference in the poll info structure as fds private as well + * for use during poll teardown as well. + */ + + fds->priv = (FAR void *)info; + + /* Check for read data availability now */ + + if (!IOB_QEMPTY(&conn->readahead)) + { + /* Normal data may be read without blocking. */ + + fds->revents |= (POLLRDNORM & fds->events); + } + + /* Check if any requested events are already in effect */ + + if (fds->revents != 0) + { + /* Yes.. then signal the poll logic */ + + nxsem_post(fds->sem); + } + + net_unlock(); + return OK; + +errout_with_lock: + kmm_free(info); + net_unlock(); + return ret; +} + +/**************************************************************************** + * Name: icmpv6_pollteardown + * + * Description: + * Teardown monitoring of events on an ICMP socket + * + * Input Parameters: + * psock - The IPPROTO_ICMP socket of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +int icmpv6_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds) +{ + FAR struct icmpv6_conn_s *conn; + FAR struct icmpv6_poll_s *info; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && + fds != NULL && fds->priv != NULL); + + conn = psock->s_conn; + + /* Recover the socket descriptor poll state info from the poll structure */ + + info = (FAR struct icmpv6_poll_s *)fds->priv; + DEBUGASSERT(info != NULL && info->fds != NULL && info->cb != NULL); + + if (info != NULL) + { + /* Release the callback */ + + net_lock(); + icmpv6_callback_free(conn->dev, info->cb); + net_unlock(); + + /* Release the poll/select data slot */ + + info->fds->priv = NULL; + + /* Then free the poll info container */ + + kmm_free(info); + } + + return OK; +} + +#endif /* !CONFIG_MM_IOB */ diff --git a/net/icmpv6/icmpv6_ping.c b/net/icmpv6/icmpv6_ping.c deleted file mode 100644 index a2e4150b0b..0000000000 --- a/net/icmpv6/icmpv6_ping.c +++ /dev/null @@ -1,502 +0,0 @@ -/**************************************************************************** - * net/icmpv6/icmpv6_ping.c - * - * Copyright (C) 2015-2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#ifdef CONFIG_NET_ICMPv6_PING - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netdev/netdev.h" -#include "devif/devif.h" -#include "inet/inet.h" -#include "utils/utils.h" -#include "icmpv6/icmpv6.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define ETHBUF ((struct eth_hdr_s *)&dev->d_buf[0]) -#define ICMPv6BUF ((struct icmpv6_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define ICMPv6ECHOREQ \ - ((struct icmpv6_echo_request_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) -#define ICMPv6ECHOREPLY \ - ((struct icmpv6_echo_reply_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct icmpv6_ping_s -{ - FAR struct devif_callback_s *png_cb; /* Reference to callback instance */ - - sem_t png_sem; /* Use to manage the wait for the response */ - systime_t png_time; /* Start time for determining timeouts */ - systime_t png_ticks; /* System clock ticks to wait */ - int png_result; /* 0: success; <0:negated errno on fail */ - net_ipv6addr_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 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 */ -}; - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ping_timeout - * - * Description: - * Check for send timeout. - * - * Parameters: - * pstate - Ping state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * The network is locked - * - ****************************************************************************/ - -static inline int ping_timeout(FAR struct icmpv6_ping_s *pstate) -{ - systime_t elapsed = clock_systimer() - pstate->png_time; - if (elapsed >= pstate->png_ticks) - { - return TRUE; - } - - return FALSE; -} - -/**************************************************************************** - * Name: icmpv6_echo_request - * - * Description: - * Format an ICMPv6 Echo Request message.. This version - * is for a standalone solicitation. If formats: - * - * - The Ethernet header - * - The IPv6 header - * - The ICMPv6 Echo Request Message - * - * Parameters: - * dev - Reference to an Ethernet device driver structure - * pstate - Ping state structure - * - * Return: - * None - * - ****************************************************************************/ - -static void icmpv6_echo_request(FAR struct net_driver_s *dev, - FAR struct icmpv6_ping_s *pstate) -{ - FAR struct icmpv6_iphdr_s *icmp; - FAR struct icmpv6_echo_request_s *req; - uint16_t reqlen; - int i; - - ninfo("Send ECHO request: seqno=%d\n", pstate->png_seqno); - - /* Set up the IPv6 header (most is probably already in place) */ - - icmp = ICMPv6BUF; - icmp->vtc = 0x60; /* Version/traffic class (MS) */ - icmp->tcf = 0; /* Traffic class (LS)/Flow label (MS) */ - icmp->flow = 0; /* Flow label (LS) */ - - /* Length excludes the IPv6 header */ - - reqlen = SIZEOF_ICMPV6_ECHO_REQUEST_S(pstate->png_datlen); - icmp->len[0] = (reqlen >> 8); - icmp->len[1] = (reqlen & 0xff); - - icmp->proto = IP_PROTO_ICMP6; /* Next header */ - icmp->ttl = IP_TTL; /* Hop limit */ - - /* Set the multicast destination IP address */ - - net_ipv6addr_copy(icmp->destipaddr, pstate->png_addr); - - /* Add out IPv6 address as the source address */ - - net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); - - /* Set up the ICMPv6 Echo Request message */ - - req = ICMPv6ECHOREQ; - req->type = ICMPv6_ECHO_REQUEST; /* Message type */ - req->code = 0; /* Message qualifier */ - req->id = htons(pstate->png_id); - req->seqno = htons(pstate->png_seqno); - - /* Add some easily verifiable data */ - - for (i = 0; i < pstate->png_datlen; i++) - { - req->data[i] = i; - } - - /* Calculate the checksum over both the ICMP header and payload */ - - icmp->chksum = 0; - icmp->chksum = ~icmpv6_chksum(dev); - - /* Set the size to the size of the IPv6 header and the payload size */ - - IFF_SET_IPv6(dev->d_flags); - - dev->d_sndlen = reqlen; - dev->d_len = reqlen + IPv6_HDRLEN; - - ninfo("Outgoing ICMPv6 Echo Request length: %d (%d)\n", - dev->d_len, (icmp->len[0] << 8) | icmp->len[1]); - -#ifdef CONFIG_NET_STATISTICS - g_netstats.icmpv6.sent++; - g_netstats.ipv6.sent++; -#endif -} - -/**************************************************************************** - * Name: ping_eventhandler - * - * Description: - * This function is called from the interrupt level to perform the actual - * ECHO request and/or ECHO reply actions when polled by the lower, device - * interfacing layer. - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * conn The received packet, cast to void * - * pvpriv An instance of struct icmpv6_ping_s cast to void* - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * Modified value of the input flags - * - * Assumptions: - * The network is locked - * - ****************************************************************************/ - -static uint16_t ping_eventhandler(FAR struct net_driver_s *dev, - FAR void *conn, - FAR void *pvpriv, uint16_t flags) -{ - FAR struct icmpv6_ping_s *pstate = (struct icmpv6_ping_s *)pvpriv; - - ninfo("flags: %04x\n", flags); - - if (pstate) - { - /* Check if the network is still up */ - - if ((flags & NETDEV_DOWN) != 0) - { - nerr("ERROR: Interface is down\n"); - pstate->png_result = -ENETUNREACH; - goto end_wait; - } - - /* Check if this is a ICMPv6 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. - */ - - else if ((flags & ICMPv6_ECHOREPLY) != 0 && conn != NULL) - { - FAR struct icmpv6_echo_reply_s *reply = ICMPv6ECHOREPLY; - - ninfo("ECHO reply: id=%d seqno=%d\n", - ntohs(reply->id), ntohs(reply->seqno)); - - if (ntohs(reply->id) == pstate->png_id) - { - /* Consume the ECHOREPLY */ - - flags &= ~ICMPv6_ECHOREPLY; - dev->d_len = 0; - - /* Return the result to the caller */ - - pstate->png_result = OK; - pstate->png_seqno = ntohs(reply->seqno); - goto end_wait; - } - } - - /* Check: - * If the outgoing packet is available (it may have been claimed - * by a sendto interrupt serving a different thread) - * -OR- - * If the output buffer currently contains unprocessed incoming - * data. - * -OR- - * If we have already sent the ECHO request - * - * In the first two cases, we will just have to wait for the next - * polling cycle. - */ - - if (dev->d_sndlen <= 0 && /* Packet available */ - (flags & ICMPv6_NEWDATA) == 0 && /* No incoming data */ - !pstate->png_sent) /* Request not sent */ - { - /* Send the ECHO request now. */ - - icmpv6_echo_request(dev, pstate); - pstate->png_sent = true; - return flags; - } - - /* Check if the selected timeout has elapsed */ - - if (ping_timeout(pstate)) - { - int failcode; - - /* Check if this device is on the same network as the destination - * device. - */ - - if (!net_ipv6addr_maskcmp(pstate->png_addr, dev->d_ipv6addr, - dev->d_ipv6netmask)) - { - /* Destination address was not on the local network served by - * this device. If a timeout occurs, then the most likely - * reason is that the destination address is not reachable. - */ - - nerr("ERROR: Not reachable\n"); - failcode = -ENETUNREACH; - } - else - { - nerr("ERROR: Ping timeout\n"); - failcode = -ETIMEDOUT; - } - - /* Report the failure */ - - pstate->png_result = failcode; - goto end_wait; - } - - /* Continue waiting */ - } - - return flags; - -end_wait: - ninfo("Resuming\n"); - - /* Do not allow any further callbacks */ - - pstate->png_cb->flags = 0; - pstate->png_cb->priv = NULL; - pstate->png_cb->event = NULL; - - /* Wake up the waiting thread */ - - nxsem_post(&pstate->png_sem); - return flags; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imcpv6_ping - * - * Description: - * Send a ECHO request and wait for the ECHO response - * - * Parameters: - * addr - The IP address of the peer to send the ICMPv6 ECHO request to - * in network order. - * id - The ID to use in the ICMPv6 ECHO request. This number should be - * unique; only ECHO responses with this matching ID will be - * processed (host order) - * seqno - The sequence number used in the ICMPv6 ECHO request. NOT used - * to match responses (host order) - * dsecs - Wait up to this many deci-seconds for the ECHO response to be - * returned (host order). - * - * Return: - * seqno of received ICMPv6 ECHO with matching ID (may be different - * from the seqno argument (may be a delayed response from an earlier - * ping with the same ID). Or a negated errno on any failure. - * - * Assumptions: - * Called from the user level with interrupts enabled. - * - ****************************************************************************/ - -int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, - uint16_t datalen, int dsecs) -{ - FAR struct net_driver_s *dev; - struct icmpv6_ping_s state; - -#ifdef CONFIG_NET_ICMPv6_NEIGHBOR - int ret; - - /* Make sure that the IP address mapping is in the Neighbor Table */ - - ret = icmpv6_neighbor(addr); - if (ret < 0) - { - nerr("ERROR: Not reachable\n"); - return -ENETUNREACH; - } -#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */ - - /* Get the device that will be used to route this ICMP ECHO request */ - - dev = netdev_findby_ipv6addr(g_ipv6_allzeroaddr, addr); - if (dev == 0) - { - nerr("ERROR: Not reachable\n"); - return -ENETUNREACH; - } - - /* Initialize the state structure */ - - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - nxsem_init(&state.png_sem, 0, 0); - nxsem_setprotocol(&state.png_sem, SEM_PRIO_NONE); - - state.png_ticks = DSEC2TICK(dsecs); /* System ticks to wait */ - state.png_result = -ENOMEM; /* Assume allocation failure */ - state.png_id = id; /* The ID to use in the ECHO request */ - state.png_seqno = seqno; /* The seqno to use in the ECHO request */ - state.png_datlen = datalen; /* The length of data to send in the ECHO request */ - state.png_sent = false; /* ECHO request not yet sent */ - - net_ipv6addr_copy(state.png_addr, addr); /* Address of the peer to be ping'ed */ - - net_lock(); - state.png_time = clock_systimer(); - - /* Set up the callback */ - - state.png_cb = icmpv6_callback_alloc(dev); - if (state.png_cb) - { - state.png_cb->flags = (ICMPv6_POLL | ICMPv6_ECHOREPLY); - state.png_cb->priv = (FAR void *)&state; - state.png_cb->event = ping_eventhandler; - state.png_result = -EINTR; /* Assume sem-wait interrupted by signal */ - - /* Notify the device driver of the availability of TX data */ - - netdev_txnotify_dev(dev); - - /* Wait for either the full round trip transfer to complete or - * for timeout to occur. (1) net_lockedwait will also terminate if a - * signal is received, (2) interrupts may be disabled! They will - * be re-enabled while the task sleeps and automatically - * re-enabled when the task restarts. - */ - - ninfo("Start time: 0x%08x seqno: %d\n", state.png_time, seqno); - net_lockedwait(&state.png_sem); - - icmpv6_callback_free(dev, state.png_cb); - } - - net_unlock(); - - /* Return the negated error number in the event of a failure, or the - * sequence number of the ECHO reply on success. - */ - - if (!state.png_result) - { - ninfo("Return seqno=%d\n", state.png_seqno); - return (int)state.png_seqno; - } - else - { - nerr("ERROR: Return error=%d\n", -state.png_result); - return state.png_result; - } -} - -#endif /* CONFIG_NET_ICMPv6_PING */ diff --git a/net/icmpv6/icmpv6_poll.c b/net/icmpv6/icmpv6_poll.c index 10b7beca0d..39af5d75c0 100644 --- a/net/icmpv6/icmpv6_poll.c +++ b/net/icmpv6/icmpv6_poll.c @@ -38,7 +38,7 @@ ****************************************************************************/ #include -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#if defined(CONFIG_NET_ICMPv6_SOCKET) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) #include #include @@ -100,4 +100,4 @@ void icmpv6_poll(FAR struct net_driver_s *dev) (void)devif_conn_event(dev, NULL, ICMPv6_POLL, dev->d_conncb); } -#endif /* CONFIG_NET_ICMPv6_PING || CONFIG_NET_ICMPv6_NEIGHBOR */ +#endif /* CONFIG_NET_ICMPv6_SOCKET || CONFIG_NET_ICMPv6_NEIGHBOR */ diff --git a/net/icmpv6/icmpv6_recvfrom.c b/net/icmpv6/icmpv6_recvfrom.c new file mode 100644 index 0000000000..79715ca245 --- /dev/null +++ b/net/icmpv6/icmpv6_recvfrom.c @@ -0,0 +1,556 @@ +/**************************************************************************** + * net/icmpv6/icmpv6_recvfrom.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "devif/devif.h" +#include "socket/socket.h" +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_NET_ICMPv6_SOCKET + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv6_BUF \ + ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPv6_BUF \ + ((struct icmpv6_echo_reply_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +#define ICMPv6_SIZE \ + ((dev)->d_len - IPv6_HDRLEN) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct icmpv6_recvfrom_s +{ + FAR struct devif_callback_s *recv_cb; /* Reference to callback instance */ + FAR struct socket *recv_sock; /* IPPROTO_ICMP6 socket structure */ + sem_t recv_sem; /* Use to manage the wait for the response */ + systime_t recv_time; /* Start time for determining timeouts */ + struct in6_addr recv_from; /* The peer we received the request from */ + FAR uint8_t *recv_buf; /* Location to return the response */ + uint16_t recv_buflen; /* Size of the response */ + int16_t recv_result; /* >=0: receive size on success; + * <0:negated errno on fail */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: recvfrom_timeout + * + * Description: + * Check for send timeout. + * + * Parameters: + * pstate - Reference to instance ot recvfrom state structure + * + * Returned Value: + * true: timeout false: no timeout + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SOCKOPTS +static inline int recvfrom_timeout(FAR struct icmpv6_recvfrom_s *pstate) +{ + FAR struct socket *psock; + + /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). + * If none... we will let the send wait forever. + */ + + psock = pstate->recv_sock; + if (psock != NULL && psock->s_rcvtimeo != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->recv_time, psock->s_rcvtimeo); + } + + /* No timeout */ + + return false; +} +#endif /* CONFIG_NET_SOCKOPTS */ + +/**************************************************************************** + * Name: recvfrom_eventhandler + * + * Description: + * This function is called from the interrupt level to perform the actual + * ECHO request and/or ECHO reply actions when polled by the lower, device + * interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * conn The received packet, cast to void * + * pvpriv An instance of struct icmpv6_recvfrom_s cast to void* + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * Modified value of the input flags + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct icmpv6_recvfrom_s *pstate = (struct icmpv6_recvfrom_s *)pvpriv; + FAR struct socket *psock; + FAR struct icmpv6_conn_s *conn; + FAR struct ipv6_hdr_s *ipv6; + FAR struct icmpv6_echo_reply_s *icmpv6; + + ninfo("flags: %04x\n", flags); + + if (pstate != NULL) + { + /* Check if the network is still up */ + + if ((flags & NETDEV_DOWN) != 0) + { + nerr("ERROR: Interface is down\n"); + pstate->recv_result = -ENETUNREACH; + goto end_wait; + } + + /* Is this a response on the same device that we sent the request out + * on? + */ + + psock = pstate->recv_sock; + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = psock->s_conn; + if (dev != conn->dev) + { + ninfo("Wrong device\n"); + return flags; + } + + /* Check if we have just received a ICMPv6 ECHO reply. */ + + if ((flags & ICMPv6_ECHOREPLY) != 0) /* No incoming data */ + { + unsigned int recvsize; + + /* Check if it is for us */ + + icmpv6 = ICMPv6_BUF; + if (conn->id != icmpv6->id) + { + ninfo("Wrong ID: %u vs %u\n", icmpv6->id, conn->id); + return flags; + } + + ninfo("Received ICMPv6 reply\n"); + + /* What should we do if the received reply is larger that the + * buffer that the caller of sendto provided? Truncate? Error + * out? + */ + + recvsize = ICMPv6_SIZE; + if (recvsize > pstate->recv_buflen) + { + recvsize = pstate->recv_buflen; + } + + /* Copy the ICMPv6 ECHO reply to the user provided buffer */ + + memcpy(pstate->recv_buf, ICMPv6_BUF, recvsize); + + /* Return the size of the returned data */ + + DEBUGASSERT(recvsize > INT16_MAX); + pstate->recv_result = recvsize; + + /* Return the IPv6 address of the sender from the IPv6 header */ + + ipv6 = IPv6_BUF; + net_ipv6addr_hdrcopy(&pstate->recv_from, ipv6->srcipaddr); + + /* Decrement the count of oustanding requests. I suppose this + * could have already been decremented of there were multiple + * threads calling sendto() or recvfrom(). If there finds, we + * may have to beef up the design. + */ + + DEBUGASSERT(conn->nreqs > 0); + conn->nreqs--; + goto end_wait; + } + +#ifdef CONFIG_NET_SOCKOPTS + /* Check if the selected timeout has elapsed */ + + if (recvfrom_timeout(pstate)) + { + nerr("ERROR: recvfrom() timeout\n"); + pstate->recv_result = -ETIMEDOUT; + goto end_wait; + } +#endif + + /* Continue waiting */ + } + + return flags; + +end_wait: + ninfo("Resuming\n"); + + /* Do not allow any further callbacks */ + + pstate->recv_cb->flags = 0; + pstate->recv_cb->priv = NULL; + pstate->recv_cb->event = NULL; + + /* Wake up the waiting thread */ + + nxsem_post(&pstate->recv_sem); + return flags; +} + +/**************************************************************************** + * Name: icmpv6_readahead + * + * Description: + * Copy the buffered read-ahead data to the user buffer. + * + * Input Parameters: + * conn - IPPROTO_ICMP6 socket connection structure containing the read- + * ahead data. + * dev The structure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * Nunber of bytes copied to the user buffer + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static inline ssize_t icmpv6_readahead(FAR struct icmpv6_conn_s *conn, + FAR void *buf, size_t buflen, + FAR struct sockaddr_in6 *from, + FAR socklen_t *fromlen) +{ + FAR struct sockaddr_in6 bitbucket; + FAR struct iob_s *iob; + ssize_t ret = -ENODATA; + int recvlen; + + /* Check there is any ICMPv6 replies already buffered in a read-ahead buffer. */ + + if ((iob = iob_peek_queue(&conn->readahead)) != NULL) + { + FAR struct iob_s *tmp; + uint16_t offset; + uint8_t addrsize; + + DEBUGASSERT(iob->io_pktlen > 0); + + /* Transfer that buffered data from the I/O buffer chain into + * the user buffer. + */ + + /* First get the size of the address */ + + recvlen = iob_copyout(&addrsize, iob, sizeof(uint8_t), 0); + if (recvlen != sizeof(uint8_t)) + { + ret = -EIO; + goto out; + } + + offset = sizeof(uint8_t); + + if (addrsize > sizeof(struct sockaddr_in6)) + { + ret = -EINVAL; + goto out; + } + + /* Then get address */ + + if (from == NULL) + { + from = &bitbucket; + } + + recvlen = iob_copyout((FAR uint8_t *)from, iob, addrsize, offset); + if (recvlen != addrsize) + { + ret = -EIO; + goto out; + } + + if (fromlen != NULL) + { + *fromlen = addrsize; + } + + offset += addrsize; + + /* And finally, get the buffered data */ + + ret = (ssize_t)iob_copyout(buf, iob, buflen, offset); + + ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen); + +out: + /* Remove the I/O buffer chain from the head of the read-ahead + * buffer queue. + */ + + tmp = iob_remove_queue(&conn->readahead); + DEBUGASSERT(tmp == iob); + UNUSED(tmp); + + /* And free the I/O buffer chain */ + + (void)iob_free_chain(iob); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * data gram socket with the IPPROTO_ICMP6 protocol. icmpv6_recvfrom() + * receives ICMPv6 ECHO replies for the a socket. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +ssize_t icmpv6_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + FAR struct sockaddr_in6 *inaddr; + FAR struct icmpv6_conn_s *conn; + FAR struct net_driver_s *dev; + struct icmpv6_recvfrom_s state; + ssize_t ret; + + /* Some sanity checks */ + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && buf != NULL); + + if (len < ICMPv6_HDRLEN) + { + return -EINVAL; + } + + /* If a 'from' address has been provided, verify that it is large + * enough to hold the AF_INET address. + */ + + if (from != NULL) + { + if (fromlen == NULL && *fromlen < sizeof(struct sockaddr_in6)) + { + return -EINVAL; + } + } + + /* We cannot receive a response from a device until a request has been + * sent to the devivce. + */ + + conn = psock->s_conn; + if (conn->nreqs < 1) + { + ret = -EPROTO; + goto errout; + } + + /* Check if there is buffered read-ahead data for this socket. We may have + * already received the reponse to previous command. + */ + + if (!IOB_QEMPTY(&conn->readahead)) + { + return icmpv6_readahead(conn, buf, len, + (FAR struct sockaddr_in6 *)from, fromlen); + } + + /* Initialize the state structure */ + + memset(&state, 0, sizeof(struct icmpv6_recvfrom_s)); + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&state.recv_sem, 0, 0); + nxsem_setprotocol(&state.recv_sem, SEM_PRIO_NONE); + + state.recv_sock = psock; /* The IPPROTO_ICMP6 socket instance */ + state.recv_result = -ENOMEM; /* Assume allocation failure */ + state.recv_buf = buf; /* Location to return the response */ + state.recv_buflen = len; /* Size of the response */ + + net_lock(); + state.recv_time = clock_systimer(); + + /* Get the device that was used to send the ICMPv6 request. */ + + dev = conn->dev; + DEBUGASSERT(dev != NULL); + if (dev == NULL) + { + ret = -EPROTO; + goto errout; + } + + /* Set up the callback */ + + state.recv_cb = icmpv6_callback_alloc(dev); + if (state.recv_cb) + { + state.recv_cb->flags = (ICMPv6_ECHOREPLY | NETDEV_DOWN); + state.recv_cb->priv = (FAR void *)&state; + state.recv_cb->event = recvfrom_eventhandler; + state.recv_result = -EINTR; /* Assume sem-wait interrupted by signal */ + + /* Wait for either the response to be received or for timeout to + * occur. (1) net_lockedwait will also terminate if a signal is + * received, (2) interrupts may be disabled! They will be re-enabled + * while the task sleeps and automatically re-enabled when the task + * restarts. + */ + + ninfo("Start time: 0x%08x\n", state.recv_time); + net_lockedwait(&state.recv_sem); + + icmpv6_callback_free(dev, state.recv_cb); + } + + net_unlock(); + + /* Return the negated error number in the event of a failure, or the + * number of bytes received on success. + */ + + if (state.recv_result < 0) + { + nerr("ERROR: Return error=%d\n", state.recv_result); + ret = state.recv_result; + goto errout; + } + + if (from != NULL) + { + inaddr = (FAR struct sockaddr_in6 *)from; + inaddr->sin6_family = AF_INET6; + inaddr->sin6_port = 0; + + net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16, + state.recv_from.s6_addr16); + } + + ret = state.recv_result; + + /* If there a no further outstanding requests, make sure that the request + * struct is left pristine. + */ + +errout: + if (conn->nreqs < 1) + { + conn->id = 0; + conn->nreqs = 0; + conn->dev = NULL; + + iob_free_queue(&conn->readahead); + } + + return ret; +} + +#endif /* CONFIG_NET_ICMPv6_SOCKET */ diff --git a/net/icmpv6/icmpv6_sendto.c b/net/icmpv6/icmpv6_sendto.c new file mode 100644 index 0000000000..bd876e86de --- /dev/null +++ b/net/icmpv6/icmpv6_sendto.c @@ -0,0 +1,509 @@ +/**************************************************************************** + * net/icmpv6/icmpv6_sendto.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "socket/socket.h" +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "inet/inet.h" +#include "arp/arp.h" +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_NET_ICMPv6_SOCKET + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv6BUF \ + ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPv6BUF \ + ((struct icmpv6_echo_request_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct icmpv6_sendto_s +{ + FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */ + FAR struct socket *snd_sock; /* IPPROTO_ICMP6 socket structure */ + sem_t snd_sem; /* Use to manage the wait for send complete */ + systime_t snd_time; /* Start time for determining timeouts */ + struct in6_addr snd_toaddr; /* The peer to send the request to */ + FAR const uint8_t *snd_buf; /* ICMPv6 header + data payload */ + uint16_t snd_buflen; /* Size of the ICMPv6 header + data payload */ + int16_t snd_result; /* 0: success; <0:negated errno on fail */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sendto_timeout + * + * Description: + * Check for send timeout. + * + * Parameters: + * pstate - Reference to instance ot sendto state structure + * + * Returned Value: + * true: timeout false: no timeout + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SOCKOPTS +static inline int sendto_timeout(FAR struct icmpv6_sendto_s *pstate) +{ + FAR struct socket *psock; + + /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). + * If none... we will let the send wait forever. + */ + + psock = pstate->snd_sock; + if (psock != NULL && psock->s_sndtimeo != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->snd_time, psock->s_sndtimeo); + } + + /* No timeout */ + + return false; +} +#endif /* CONFIG_NET_SOCKOPTS */ + +/**************************************************************************** + * Name: sendto_request + * + * Description: + * Setup to send an ICMPv6 request packet + * + * Parameters: + * dev - The device driver structure to use in the send operation + * pstate - Reference to an instance of the ICMPv6 sendto state structure + * + * Return: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void sendto_request(FAR struct net_driver_s *dev, + FAR struct icmpv6_sendto_s *pstate) +{ + FAR struct ipv6_hdr_s *ipv6; + FAR struct icmpv6_echo_request_s *icmpv6; + + IFF_SET_IPv6(dev->d_flags); + + /* The total length to send is the size of the application data plus the + * IP and ICMPv6 headers (and, eventually, the Ethernet header) + */ + + dev->d_len = IPv6_HDRLEN + pstate->snd_buflen; + + /* The total size of the data (including the size of the ICMPv6 header) */ + + dev->d_sndlen += pstate->snd_buflen; + + /* Set up the IPv6 header (most is probably already in place) */ + + ipv6 = IPv6BUF; + ipv6->vtc = 0x60; /* Version/traffic class (MS) */ + ipv6->tcf = 0; /* Traffic class(LS)/Flow label(MS) */ + ipv6->flow = 0; /* Flow label (LS) */ + ipv6->len[0] = (pstate->snd_buflen >> 8); /* Length excludes the IPv6 header */ + ipv6->len[1] = (pstate->snd_buflen & 0xff); + ipv6->proto = IP_PROTO_ICMP6; /* Next header */ + ipv6->ttl = 255; /* Hop limit */ + + net_ipv6addr_hdrcopy(ipv6->srcipaddr, dev->d_ipv6addr); + net_ipv6addr_hdrcopy(ipv6->destipaddr, pstate->snd_toaddr.s6_addr16); + + /* Copy the ICMPv6 request and payload into place after the IPv6 header */ + + icmpv6 = ICMPv6BUF; + memcpy(icmpv6, pstate->snd_buf, pstate->snd_buflen); + + /* Calculate the ICMPv6 checksum over the ICMPv6 header and payload. */ + + icmpv6->chksum = 0; + icmpv6->chksum = ~icmpv6_chksum(dev); + if (icmpv6->chksum == 0) + { + icmpv6->chksum = 0xffff; + } + + ninfo("Outgoing ICMPv6 packet length: %d (%d)\n", + dev->d_len, (ipv6->len[0] << 8) | ipv6->len[1]); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.icmpv6.sent++; + g_netstats.ipv6.sent++; +#endif +} + +/**************************************************************************** + * Name: sendto_eventhandler + * + * Description: + * This function is called from the interrupt level to perform the actual + * ECHO request and/or ECHO reply actions when polled by the lower, device + * interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * conn The received packet, cast to void * + * pvpriv An instance of struct icmpv6_sendto_s cast to void* + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * Modified value of the input flags + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, + FAR void *conn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct icmpv6_sendto_s *pstate = (struct icmpv6_sendto_s *)pvpriv; + + ninfo("flags: %04x\n", flags); + + if (pstate != NULL) + { + /* Check if the network is still up */ + + if ((flags & NETDEV_DOWN) != 0) + { + nerr("ERROR: Interface is down\n"); + pstate->snd_result = -ENETUNREACH; + goto end_wait; + } + + /* Check: + * If the outgoing packet is available (it may have been claimed + * by a sendto interrupt serving a different thread) + * -OR- + * If the output buffer currently contains unprocessed incoming + * data. + * -OR- + * If we have already sent the ECHO request + * + * In the first two cases, we will just have to wait for the next + * polling cycle. + */ + + if (dev->d_sndlen <= 0 && /* Packet available */ + (flags & ICMPv6_NEWDATA) == 0) /* No incoming data */ + { + /* Send the ICMPv6 echo request. */ + + ninfo("Send ICMPv6 ECHO request\n"); + + sendto_request(dev, pstate); + pstate->snd_result = OK; + goto end_wait; + } + +#ifdef CONFIG_NET_SOCKOPTS + /* Check if the selected timeout has elapsed */ + + if (sendto_timeout(pstate)) + { + int failcode; + + /* Check if this device is on the same network as the destination + * device. + */ + + if (!net_ipv6addr_maskcmp(pstate->snd_toaddr.s6_addr16, + dev->d_ipv6addr, dev->d_ipv6netmask)) + { + /* Destination address was not on the local network served by this + * device. If a timeout occurs, then the most likely reason is + * that the destination address is not reachable. + */ + + nerr("ERROR: Not reachable\n"); + failcode = -ENETUNREACH; + } + else + { + nerr("ERROR: sendto() timeout\n"); + failcode = -ETIMEDOUT; + } + + /* Report the failure */ + + pstate->snd_result = failcode; + goto end_wait; + } +#endif + + /* Continue waiting */ + } + + return flags; + +end_wait: + ninfo("Resuming\n"); + + /* Do not allow any further callbacks */ + + pstate->snd_cb->flags = 0; + pstate->snd_cb->priv = NULL; + pstate->snd_cb->event = NULL; + + /* Wake up the waiting thread */ + + nxsem_post(&pstate->snd_sem); + return flags; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_sendto + * + * Description: + * Implements the sendto() operation for the case of the IPPROTO_ICMP6 + * socket. The 'buf' parameter points to a block of memory that includes + * an ICMPv6 request header, followed by any payload that accompanies the + * request. The 'len' parameter includes both the size of the ICMPv6 header + * and the following payload. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send_to() for the list of appropriate error + * values. + * + ****************************************************************************/ + +ssize_t icmpv6_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, + int flags, FAR const struct sockaddr *to, socklen_t tolen) +{ + FAR const struct sockaddr_in6 *inaddr; + FAR struct net_driver_s *dev; + FAR struct icmpv6_conn_s *conn; + FAR struct icmpv6_echo_request_s *icmpv6; + struct icmpv6_sendto_s state; + int ret; + + /* Some sanity checks */ + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && + buf != NULL && to != NULL); + + if (len < ICMPv6_HDRLEN || tolen < sizeof(struct sockaddr_in6)) + { + return -EINVAL; + } + + conn = psock->s_conn; + inaddr = (FAR const struct sockaddr_in6 *)to; + + /* Get the device that will be used to route this ICMPv6 ECHO request */ + + dev = netdev_findby_ipv6addr(g_ipv6_allzeroaddr, inaddr->sin6_addr.s6_addr16); + if (dev == NULL) + { + nerr("ERROR: Not reachable\n"); + ret = -ENETUNREACH; + goto errout; + } + + /* If we are no longer processing the same ping ID, then flush any pending + * packets from the read-ahead buffer. + * + * REVISIT: How to we free up any lingering reponses if there are no + * futher pings? + */ + + icmpv6 = (FAR struct icmpv6_echo_request_s *)buf; + if (icmpv6->type != ICMPv6_ECHO_REQUEST || icmpv6->id != conn->id || + dev != conn->dev) + { + conn->id = 0; + conn->nreqs = 0; + conn->dev = NULL; + + iob_free_queue(&conn->readahead); + } + +#ifdef CONFIG_NET_ICMPv6_NEIGHBOR + /* Make sure that the IP address mapping is in the Neighbor Table */ + + ret = icmpv6_neighbor(inaddr->sin6_addr.s6_addr16); + if (ret < 0) + { + nerr("ERROR: Not reachable\n"); + ret = -ENETUNREACH; + goto errout; + } +#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */ + + /* Initialize the state structure */ + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&state.snd_sem, 0, 0); + nxsem_setprotocol(&state.snd_sem, SEM_PRIO_NONE); + + state.snd_sock = psock; /* The IPPROTO_ICMP6 socket instance */ + state.snd_result = -ENOMEM; /* Assume allocation failure */ + state.snd_buf = buf; /* ICMPv6 header + data payload */ + state.snd_buflen = len; /* Size of the ICMPv6 header+data payload */ + + net_ipv6addr_copy(state.snd_toaddr.s6_addr16, + inaddr->sin6_addr.s6_addr16); + + net_lock(); + state.snd_time = clock_systimer(); + + /* Set up the callback */ + + state.snd_cb = icmpv6_callback_alloc(dev); + if (state.snd_cb) + { + state.snd_cb->flags = (ICMPv6_POLL | NETDEV_DOWN); + state.snd_cb->priv = (FAR void *)&state; + state.snd_cb->event = sendto_eventhandler; + state.snd_result = -EINTR; /* Assume sem-wait interrupted by signal */ + + /* Setup to receive ICMPv6 ECHO replies */ + + if (icmpv6->type == ICMPv6_ECHO_REQUEST) + { + conn->id = icmpv6->id; + conn->nreqs = 1; + } + + conn->dev = dev; + + /* Notify the device driver of the availability of TX data */ + + netdev_txnotify_dev(dev); + + /* Wait for either the send to complete or for timeout to occur. (1) + * net_lockedwait will also terminate if a signal is received, (2) + * interrupts may be disabled! They will be re-enabled while the + * task sleeps and automatically re-enabled when the task restarts. + */ + + ninfo("Start time: 0x%08x\n", state.snd_time); + net_lockedwait(&state.snd_sem); + + icmpv6_callback_free(dev, state.snd_cb); + } + + net_unlock(); + + /* Return the negated error number in the event of a failure, or the + * number of bytes sent on success. + */ + + if (state.snd_result < 0) + { + nerr("ERROR: Return error=%d\n", state.snd_result); + ret = state.snd_result; + goto errout; + } + + return len; + +errout: + conn->id = 0; + conn->nreqs = 0; + conn->dev = NULL; + + iob_free_queue(&conn->readahead); + return ret; +} + +#endif /* CONFIG_NET_ICMPv6_SOCKET */ diff --git a/net/icmpv6/icmpv6_sockif.c b/net/icmpv6/icmpv6_sockif.c new file mode 100644 index 0000000000..8036bc1c2f --- /dev/null +++ b/net/icmpv6/icmpv6_sockif.c @@ -0,0 +1,520 @@ +/**************************************************************************** + * net/socket/icmpv6_sockif.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_NET_ICMPv6_SOCKET + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int icmpv6_setup(FAR struct socket *psock, int protocol); +static sockcaps_t icmpv6_sockcaps(FAR struct socket *psock); +static void icmpv6_addref(FAR struct socket *psock); +static int icmpv6_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int icmpv6_getsockname(FAR struct socket *psock, + FAR struct sockaddr *addr, FAR socklen_t *addrlen); +static int icmpv6_listen(FAR struct socket *psock, int backlog); +static int icmpv6_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int icmpv6_accept(FAR struct socket *psock, + FAR struct sockaddr *addr, FAR socklen_t *addrlen, + FAR struct socket *newsock); +#ifndef CONFIG_DISABLE_POLL +static int icmpv6_netpoll(FAR struct socket *psock, + FAR struct pollfd *fds, bool setup); +#endif +static ssize_t icmpv6_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static int icmpv6_close(FAR struct socket *psock); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct sock_intf_s g_icmpv6_sockif = +{ + icmpv6_setup, /* si_setup */ + icmpv6_sockcaps, /* si_sockcaps */ + icmpv6_addref, /* si_addref */ + icmpv6_bind, /* si_bind */ + icmpv6_getsockname, /* si_getsockname */ + icmpv6_listen, /* si_listen */ + icmpv6_connect, /* si_connect */ + icmpv6_accept, /* si_accept */ +#ifndef CONFIG_DISABLE_POLL + icmpv6_netpoll, /* si_poll */ +#endif + icmpv6_send, /* si_send */ + icmpv6_sendto, /* si_sendto */ +#ifdef CONFIG_NET_SENDFILE + NULL, /* si_sendfile */ +#endif + icmpv6_recvfrom, /* si_recvfrom */ + icmpv6_close /* si_close */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_setup + * + * Description: + * Called for socket() to verify that the provided socket type and + * protocol are usable by this address family. Perform any family- + * specific socket fields. + * + * Parameters: + * psock A pointer to a user allocated socket structure to be + * initialized. + * protocol (see sys/socket.h) + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise, a negater errno value is + * returned. + * + ****************************************************************************/ + +static int icmpv6_setup(FAR struct socket *psock, int protocol) +{ + /* Only SOCK_DGRAM and IPPROTO_ICMP6 are supported */ + + if (psock->s_type == SOCK_DGRAM && protocol == IPPROTO_ICMP6) + { + /* Allocate the IPPROTO_ICMP6 socket connection structure and save in + * the new socket instance. + */ + + FAR struct icmpv6_conn_s *conn = icmpv6_alloc(); + if (conn == NULL) + { + /* Failed to reserve a connection structure */ + + return -ENOMEM; + } + + /* Set the reference count on the connection structure. + * This reference count will be incremented only if the socket is + * dup'ed + */ + + DEBUGASSERT(conn->crefs == 0); + conn->crefs = 1; + + /* Save the pre-allocated connection in the socket structure */ + + psock->s_conn = conn; + return OK; + } + else + { + return -EPROTONOSUPPORT; + } +} + +/**************************************************************************** + * Name: icmpv6_sockcaps + * + * Description: + * Return the bit encoded capabilities of this socket. + * + * Parameters: + * psock - Socket structure of the socket whose capabilities are being + * queried. + * + * Returned Value: + * The set of socket cababilities is returned. + * + ****************************************************************************/ + +static sockcaps_t icmpv6_sockcaps(FAR struct socket *psock) +{ + return 0; +} + +/**************************************************************************** + * Name: icmpv6_addref + * + * Description: + * Increment the refernce count on the underlying connection structure. + * + * Parameters: + * psock - Socket structure of the socket whose reference count will be + * incremented. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void icmpv6_addref(FAR struct socket *psock) +{ + FAR struct icmpv6_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + + conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; +} + +/**************************************************************************** + * Name: icmpv6_connect + * + * Description: + * icmpv6_connect() connects the local socket referred to by the structure + * 'psock' to the address specified by 'addr'. The addrlen argument + * specifies the size of 'addr'. The format of the address in 'addr' is + * determined by the address space of the socket 'psock'. + * + * If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address + * to which datagrams are sent by default, and the only address from which + * datagrams are received. If the socket is of type SOCK_STREAM or + * SOCK_SEQPACKET, this call attempts to make a connection to the socket + * that is bound to the address specified by 'addr'. + * + * Generally, connection-based protocol sockets may successfully + * icmpv6_connect() only once; connectionless protocol sockets may use + * icmpv6_connect() multiple times to change their association. + * Connectionless sockets may dissolve the association by connecting to + * an address with the sa_family member of sockaddr set to AF_UNSPEC. + * + * Parameters: + * psock Pointer to a socket structure initialized by psock_socket() + * addr Server address (form depends on type of socket) + * addrlen Length of actual 'addr' + * + * Returned Value: + * 0 on success; a negated errno value on failue. See connect() for the + * list of appropriate errno values to be returned. + * + ****************************************************************************/ + +static int icmpv6_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + return -EAFNOSUPPORT; +} + +/**************************************************************************** + * Name: icmpv6_accept + * + * Description: + * The icmpv6_accept function is used with connection-based socket types + * (SOCK_STREAM, SOCK_SEQPACKET and SOCK_RDM). It extracts the first + * connection request on the queue of pending connections, creates a new + * connected socket with mostly the same properties as 'sockfd', and + * allocates a new socket descriptor for the socket, which is returned. The + * newly created socket is no longer in the listening state. The original + * socket 'sockfd' is unaffected by this call. Per file descriptor flags + * are not inherited across an icmpv6_accept. + * + * The 'sockfd' argument is a socket descriptor that has been created with + * socket(), bound to a local address with bind(), and is listening for + * connections after a call to listen(). + * + * On return, the 'addr' structure is filled in with the address of the + * connecting entity. The 'addrlen' argument initially contains the size + * of the structure pointed to by 'addr'; on return it will contain the + * actual length of the address returned. + * + * If no pending connections are present on the queue, and the socket is + * not marked as non-blocking, icmpv6_accept blocks the caller until a + * connection is present. If the socket is marked non-blocking and no + * pending connections are present on the queue, icmpv6_accept returns + * EAGAIN. + * + * Parameters: + * psock Reference to the listening socket structure + * addr Receives the address of the connecting client + * addrlen Input: allocated size of 'addr', Return: returned size of 'addr' + * newsock Location to return the accepted socket information. + * + * Returned Value: + * Returns 0 (OK) on success. On failure, it returns a negated errno + * value. See accept() for a desrciption of the approriate error value. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int icmpv6_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock) +{ + return -EAFNOSUPPORT; +} + +/**************************************************************************** + * Name: icmpv6_bind + * + * Description: + * icmpv6_bind() gives the socket 'psock' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a + * name to a socket." When a socket is created with socket(), it exists + * in a name space (address family) but has no name assigned. + * + * Parameters: + * psock Socket structure of the socket to bind + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; A negated errno value is returned on failure. See + * bind() for a list a appropriate error values. + * + ****************************************************************************/ + +static int icmpv6_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + /* An ICMPv6 socket cannot be bound to a local address */ + + return -EBADF; +} + +/**************************************************************************** + * Name: icmpv6_getsockname + * + * Description: + * The icmpv6_getsockname() function retrieves the locally-bound name of the + * specified packet socket, stores this address in the sockaddr structure + * pointed to by the 'addr' argument, and stores the length of this + * address in the object pointed to by the 'addrlen' argument. + * + * If the actual length of the address is greater than the length of the + * supplied sockaddr structure, the stored address will be truncated. + * + * If the socket has not been bound to a local name, the value stored in + * the object pointed to by address is unspecified. + * + * Parameters: + * psock Socket structure of the socket to be queried + * addr sockaddr structure to receive data [out] + * addrlen Length of sockaddr structure [in/out] + * + * Returned Value: + * On success, 0 is returned, the 'addr' argument points to the address + * of the socket, and the 'addrlen' argument points to the length of the + * address. Otherwise, a negated errno value is returned. See + * getsockname() for the list of appropriate error numbers. + * + ****************************************************************************/ + +static int icmpv6_getsockname(FAR struct socket *psock, + FAR struct sockaddr *addr, FAR socklen_t *addrlen) +{ + return -EAFNOSUPPORT; +} + +/**************************************************************************** + * Name: icmpv6_listen + * + * Description: + * To accept connections, a socket is first created with psock_socket(), a + * willingness to accept incoming connections and a queue limit for + * incoming connections are specified with psock_listen(), and then the + * connections are accepted with psock_accept(). For the case of raw + * packet sockets, psock_listen() calls this function. The psock_listen() + * call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET. + * + * Parameters: + * psock Reference to an internal, boound socket structure. + * backlog The maximum length the queue of pending connections may grow. + * If a connection request arrives with the queue full, the client + * may receive an error with an indication of ECONNREFUSED or, + * if the underlying protocol supports retransmission, the request + * may be ignored so that retries succeed. + * + * Returned Value: + * On success, zero is returned. On error, a negated errno value is + * returned. See list() for the set of appropriate error values. + * + ****************************************************************************/ + +int icmpv6_listen(FAR struct socket *psock, int backlog) +{ + return -EOPNOTSUPP; +} + +/**************************************************************************** + * Name: icmpv6_netpoll + * + * Description: + * The standard poll() operation redirects operations on socket descriptors + * to net_poll which, indiectly, calls to function. + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int icmpv6_netpoll(FAR struct socket *psock, FAR struct pollfd *fds, + bool setup) +{ +#ifdef CONFIG_MM_IOB + /* Check if we are setting up or tearing down the poll */ + + if (setup) + { + /* Perform the ICMPv6 poll() setup */ + + return icmpv6_pollsetup(psock, fds); + } + else + { + /* Perform the ICMPv6 poll() teardown */ + + return icmpv6_pollteardown(psock, fds); + } +#else + return -ENOSYS; +#endif /* CONFIG_MM_IOB */ +} +#endif /* !CONFIG_DISABLE_POLL */ + +/**************************************************************************** + * Name: icmpv6_send + * + * Description: + * Socket send() method for the raw packet socket. + * + * Parameters: + * psock An instance of the internal socket structure. + * buf Data to send + * len Length of data to send + * flags Send flags + * + * Returned Value: + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send() for the list of appropriate error + * values. + * + ****************************************************************************/ + +static ssize_t icmpv6_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags) +{ + /* ICMPv6 sockets cannot be bound and, hence, cannot support any connection- + * oriented data transfer. + */ + + return -EDESTADDRREQ; +} + +/**************************************************************************** + * Name: icmpv6_close + * + * Description: + * Performs the close operation on a raw packet socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; a negated errno value is returned on any failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int icmpv6_close(FAR struct socket *psock) +{ + FAR struct icmpv6_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there could be\ + * more if the socket was dup'ed). + */ + + DEBUGASSERT(conn->crefs > 0); + + if (conn->crefs <= 1) + { + /* Yes... free any read-ahead data */ + + iob_free_queue(&conn->readahead); + + /* Then free the connection structure */ + + conn->crefs = 0; /* No more references on the connection */ + icmpv6_free(psock->s_conn); /* Free network resources */ + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* CONFIG_NET_ICMPv6_SOCKET */ diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index df9b467697..761a65bb1b 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -51,6 +51,7 @@ #include "tcp/tcp.h" #include "udp/udp.h" #include "icmp/icmp.h" +#include "icmpv6/icmpv6.h" #include "sixlowpan/sixlowpan.h" #include "socket/socket.h" #include "inet/inet.h" @@ -1237,6 +1238,15 @@ FAR const struct sock_intf_s * } else #endif +#if defined(HAVE_PFINET6_SOCKETS) && defined(CONFIG_NET_ICMPv6_SOCKET) + /* PF_INET, ICMP data gram sockets are a special case of raw sockets */ + + if (family == PF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_ICMP6) + { + return &g_icmpv6_sockif; + } + else +#endif #ifdef NET_UDP_HAVE_STACK if (type == SOCK_DGRAM && (protocol == 0 || protocol == IPPROTO_UDP)) { diff --git a/net/net_initialize.c b/net/net_initialize.c index 7ed6f0b6a5..4d0105acf7 100644 --- a/net/net_initialize.c +++ b/net/net_initialize.c @@ -52,6 +52,7 @@ #include "sixlowpan/sixlowpan.h" #include "neighbor/neighbor.h" #include "icmp/icmp.h" +#include "icmpv6/icmpv6.h" #include "tcp/tcp.h" #include "udp/udp.h" #include "pkt/pkt.h" @@ -133,6 +134,12 @@ void net_setup(void) icmp_sock_initialize(); #endif +#ifdef CONFIG_NET_ICMPv6_SOCKET + /* Initialize IPPPROTO_ICMP6 socket support */ + + icmpv6_sock_initialize(); +#endif + #ifdef CONFIG_NET_IEEE802154 /* Initialize IEEE 802.15.4 socket support */