diff --git a/configs/dk-tm4c129x/ipv6/defconfig b/configs/dk-tm4c129x/ipv6/defconfig index 7980f0bb38..81b3becb22 100644 --- a/configs/dk-tm4c129x/ipv6/defconfig +++ b/configs/dk-tm4c129x/ipv6/defconfig @@ -637,6 +637,7 @@ CONFIG_NET_ICMPv6_PING=y CONFIG_NET_ICMPv6_NEIGHBOR=y CONFIG_ICMPv6_NEIGHBOR_MAXTRIES=5 CONFIG_ICMPv6_NEIGHBOR_DELAYMSEC=20 +# CONFIG_NET_ICMPv6_AUTOCONF is not set # # IGMPv2 Client Support @@ -857,8 +858,6 @@ CONFIG_EXAMPLES_NSH=y # Networking Utilities # # CONFIG_NETUTILS_CODECS is not set -# CONFIG_NETUTILS_DHCPC is not set -# CONFIG_NETUTILS_DHCPD is not set # CONFIG_NETUTILS_FTPC is not set # CONFIG_NETUTILS_FTPD is not set # CONFIG_NETUTILS_JSON is not set diff --git a/include/nuttx/net/icmpv6.h b/include/nuttx/net/icmpv6.h index 20f9223963..af17c4b95b 100644 --- a/include/nuttx/net/icmpv6.h +++ b/include/nuttx/net/icmpv6.h @@ -109,9 +109,19 @@ /* ICMPv6 Neighbor Advertisement message flags */ -#define ICMPv6_FLAG_R (1 << 7) /* Router flag */ -#define ICMPv6_FLAG_S (1 << 6) /* Solicited flag */ -#define ICMPv6_FLAG_O (1 << 5) /* Override flag */ +#define ICMPv6_NADV_FLAG_R (1 << 7) /* Router flag */ +#define ICMPv6_NADV_FLAG_S (1 << 6) /* Solicited flag */ +#define ICMPv6_NADV_FLAG_O (1 << 5) /* Override flag */ + +/* ICMPv6 Router Advertisement message flags */ + +#define ICMPv6_RADV_FLAG_M (1 << 7) /* Managed address configuration flag */ +#define ICMPv6_RADV_FLAG_O (1 << 6) /* Other configuration flag */ + +/* Prefix option flags */ + +#define ICMPv6_PRFX_FLAG_L (1 << 7) /* On-link flag */ +#define ICMPv6_PRFX_FLAG_A (1 << 6) /* Autonomous address-configuration flag /**************************************************************************** * Public Type Definitions @@ -152,8 +162,9 @@ struct icmpv6_neighbor_solicit_s uint16_t chksum; /* Checksum of ICMP header and data */ uint8_t flags[4]; /* See ICMPv6_FLAG_ definitions */ net_ipv6addr_t tgtaddr; /* 128-bit Target IPv6 address */ + uint8_t opttype; /* Option Type: ICMPv6_OPT_SRCLLADDR */ - uint8_t optlen; /* Option length: 8 octets */ + uint8_t optlen; /* Option length: 1 octet */ #ifdef CONFIG_NET_ETHERNET uint8_t srclladdr[6]; /* Options: Source link layer address */ #endif @@ -166,15 +177,53 @@ struct icmpv6_neighbor_advertise_s uint8_t type; /* Message Type: ICMPv6_NEIGHBOR_ADVERTISE */ uint8_t code; /* Further qualifies the ICMP messages */ uint16_t chksum; /* Checksum of ICMP header and data */ - uint8_t flags[4]; /* See ICMPv6_FLAG_ definitions */ + uint8_t flags[4]; /* See ICMPv6_NADV_FLAG_ definitions */ net_ipv6addr_t tgtaddr; /* Target IPv6 address */ + uint8_t opttype; /* Option Type: ICMPv6_OPT_TGTLLADDR */ - uint8_t optlen; /* Option length: 8 octets */ + uint8_t optlen; /* Option length: 1 octet */ #ifdef CONFIG_NET_ETHERNET uint8_t tgtlladdr[6]; /* Options: Target link layer address */ #endif }; +/* This the message format for the ICMPv6 Router Solicitation message */ + +struct icmpv6_router_solicit_s +{ + uint8_t type; /* Message Type: ICMPV6_ROUTER_SOLICIT */ + uint8_t code; /* Further qualifies the ICMP messages */ + uint16_t chksum; /* Checksum of ICMP header and data */ + uint8_t flags[4]; /* See ICMPv6_RADV_FLAG_ definitions (must be zero) */ + + uint8_t opttype; /* Option Type: ICMPv6_OPT_SRCLLADDR */ + uint8_t optlen; /* Option length: 1 octet */ +#ifdef CONFIG_NET_ETHERNET + uint8_t srclladdr[6]; /* Options: Source link layer address */ +#endif +}; + +/* This the message format for the ICMPv6 Router Advertisement message: + * Options may include: ICMPv6_OPT_SRCLLADDR, ICMPv6_OPT_MTU, and/or + * ICMPv6_OPT_PREFIX + */ + +struct icmpv6_router_advertise_s +{ + uint8_t type; /* Message Type: ICMPV6_ROUTER_ADVERTISE */ + uint8_t code; /* Further qualifies the ICMP messages */ + uint16_t chksum; /* Checksum of ICMP header and data */ + uint8_t hoplimit; /* Current hop limit */ + uint8_t flags; /* See ICMPv6_RADV_FLAG_* definitions */ + uint16_t lifetime; /* Router lifetime */ + uint16_t reachable[2]; /* Reachable time */ + uint16_t retrans[2]; /* Retransmission timer */ + uint8_t options[1]; /* Options begin here */ +}; + +#define ICMPv6_RADV_MINLEN (16) +#define ICMPv6_RADV_OPTLEN(n) ((n) - ICMPv6_RADV_MINLEN) + /* This the message format for the ICMPv6 Echo Request message */ struct icmpv6_echo_request_s @@ -205,6 +254,61 @@ struct icmpv6_echo_reply_s #define SIZEOF_ICMPV6_ECHO_REPLY_S(n) \ (sizeof(struct icmpv6_echo_reply_s) - 1 + (n)) +/* Option types */ + +struct icmpv6_generic_s +{ + uint8_t opttype; /* Octet 1: Option Type */ + uint8_t optlen; /* " " ": Option length (in octets) */ + uint16_t pad; /* " " ": The rest depends on the option */ +}; + +struct icmpv6_srclladdr_s +{ + uint8_t opttype; /* Octet 1: Option Type: ICMPv6_OPT_SRCLLADDR */ + uint8_t optlen; /* " " ": Option length: 1 octet */ +#ifdef CONFIG_NET_ETHERNET + uint8_t srclladdr[6]; /* " " ": Options: Source link layer address */ +#endif +}; + +struct icmpv6_tgrlladdr_s +{ + uint8_t opttype; /* Octet 1: Option Type: ICMPv6_OPT_TGTLLADDR */ + uint8_t optlen; /* " " ": Option length: 1 octet */ +#ifdef CONFIG_NET_ETHERNET + uint8_t tgtlladdr[6]; /* " " ": Options: Target link layer address */ +#endif +}; + +struct icmpv6_prefixinfo_s +{ + uint8_t opttype; /* Octet 1: Option Type: ICMPv6_OPT_PREFIX */ + uint8_t optlen; /* " " ": Option length: 4 octets */ + uint8_t preflen; /* " " ": Prefix length */ + uint8_t flags; /* " " ": Flags */ + uint16_t vlifetime[2]; /* " " ": Valid lifetime */ + uint16_t plifetime[2]; /* Octet 2: Preferred lifetime */ + uint16_t reserved[2]; /* " " ": Reserved */ + uint16_t prefix[8]; /* Octets 3-4: Prefix */ +}; + +struct icmpv6_redirect_s +{ + uint8_t opttype; /* Octet 1: Option Type: ICMPv6_OPT_REDIRECT */ + uint8_t optlen; /* " " ": Option length: 1 octet */ + uint16_t reserved[3]; /* " " ": Reserved */ + uint8_t header[1]; /* Octets 2-: Beginning of the IP header */ +}; + +struct icmpv6_mtu_s +{ + uint8_t opttype; /* Octet 1: Option Type: ICMPv6_OPT_MTU */ + uint8_t optlen; /* " " ": Option length: 1 octet */ + uint16_t reserved; /* " " ": Reserved */ + uint16_t mtu[2]; /* " " ": MTU */ +}; + /* The structure holding the ICMP statistics that are gathered if * CONFIG_NET_STATISTICS is defined. */ diff --git a/net/devif/devif.h b/net/devif/devif.h index 69df25a6f1..f0f3148e70 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -70,6 +70,7 @@ * UDP_NEWDATA OUT: Cleared (only) by the socket layer logic to indicate * PKT_NEWDATA that the new data was consumed, suppressing further * ICMP_NEWDATA attempts to process the new data. + * ICMPv6_NEWDATA * * TCP_SNDACK IN: Not used; always zero * OUT: Set by the socket layer if the new data was consumed diff --git a/net/icmpv6/Kconfig b/net/icmpv6/Kconfig index 7977776a49..f373b9f328 100644 --- a/net/icmpv6/Kconfig +++ b/net/icmpv6/Kconfig @@ -34,14 +34,14 @@ config NET_ICMPv6_NEIGHBOR if NET_ICMPv6_NEIGHBOR config ICMPv6_NEIGHBOR_MAXTRIES - int "ICMPv6 solicitation retries" + int "ICMPv6 neighbor solicitation retries" default 5 ---help--- Send the Neighbor solicitation this number of times before giving up and deciding that the target IP6 address is non reachable. config ICMPv6_NEIGHBOR_DELAYMSEC - int "ICMPv6 re-solicit delay" + int "ICMPv6 neighbor re-solicit delay" default 20 ---help--- Wait this number of milliseconds after sending the Neighbor @@ -61,6 +61,26 @@ config NET_ICMPv6_AUTOCONF ICMPv6 auto configuration is an alternative to DHCPv6 for obtaining an IPv6 address from a router. +if NET_ICMPv6_AUTOCONF + +config ICMPv6_AUTOCONF_MAXTRIES + int "ICMPv6 router solicitation retries" + default 5 + ---help--- + Send the Router solicitation this number of times before giving + up and deciding that no router is going to provide an IP address. + +config ICMPv6_AUTOCONF_DELAYMSEC + int "ICMPv6 router re-solicit delay" + default 20 + ---help--- + Wait this number of milliseconds before re-sending the Router + Solicitation. This time should be related to the maximum + round trip time on the network since it is basically the time from + when an Router Solicitation is sent until the Router + Advertisement is received. + +endif # NET_ICMPv6_AUTOCONF endif # NET_ICMPv6 endmenu # ICMPv6 Networking Support endif # NET_IPv6 diff --git a/net/icmpv6/Make.defs b/net/icmpv6/Make.defs index ce2359a624..0eb0fe1ca9 100644 --- a/net/icmpv6/Make.defs +++ b/net/icmpv6/Make.defs @@ -56,7 +56,7 @@ endif endif ifeq ($(CONFIG_NET_ICMPv6_AUTOCONF),y) -NET_CSRCS += icmpv6_autoconfig.c +NET_CSRCS += icmpv6_autoconfig.c icmpv6_rsolicit.c icmpv6_rnotify.c endif # Include ICMPv6 build support diff --git a/net/icmpv6/icmpv6.h b/net/icmpv6/icmpv6.h index 808e16a2a6..2a9a094f29 100644 --- a/net/icmpv6/icmpv6.h +++ b/net/icmpv6/icmpv6.h @@ -62,19 +62,20 @@ * Public Type Definitions ****************************************************************************/ -#if defined(CONFIG_NET_ICMPv6_PING) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#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 it is a singleton for the case of ARP packet transfers. + * provided. But, in this case, it is a singleton. */ struct icmpv6_conn_s { - FAR struct devif_callback_s *list; /* ARP callbacks */ + FAR struct devif_callback_s *list; /* Neighbor discovery callbacks */ }; #endif #ifdef CONFIG_NET_ICMPv6_NEIGHBOR -/* Used to notify a thread waiting for a particular ARP response */ +/* Used to notify a thread waiting for a particular Neighbor Advertisement */ struct icmpv6_notify_s { @@ -85,6 +86,24 @@ struct icmpv6_notify_s }; #endif +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +/* Used to notify a thread waiting for a particular Router Advertisement */ + +struct icmpv6_rnotify_s +{ +#ifdef CONFIG_NET_MULTILINK + FAR struct icmpv6_notify_s *rn_flink; /* Supports singly linked list */ +#endif + net_ipv6addr_t rn_prefix; /* Waited for router prefix */ + uint8_t rn_preflen; /* Prefix length (# valid leading bits) */ +#ifdef CONFIG_NET_MULTILINK + char rn_ifname[IFNAMSIZ]; /* Device name */ +#endif + sem_t rn_sem; /* Will wake up the waiter */ + int rn_result; /* The result of the wait */ +}; +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -211,12 +230,38 @@ void icmpv6_solicit(FAR struct net_driver_s *dev, } #endif +/**************************************************************************** + * Name: icmpv6_rsolicit + * + * Description: + * Set up to send an ICMPv6 Router Solicitation message. This version + * is for a standalone solicitation. If formats: + * + * - The Ethernet header + * - The IPv6 header + * - The ICMPv6 Neighbor Router Message + * + * The device IP address should have been set to the link local address + * prior to calling this function. + * + * Parameters: + * dev - Reference to an Ethernet device driver structure + * + * Return: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +void icmpv6_rsolicit(FAR struct net_driver_s *dev); +#endif + /**************************************************************************** * Function: icmpv6_wait_setup * * Description: * Called BEFORE an Neighbor Solicitation is sent. This function sets up - * the Neighbor Advertisement timeout before the the Neighbor Solicitation + * the Neighbor Advertisement timeout before the Neighbor Solicitation * is sent so that there is no race condition when icmpv6_wait() is called. * * Assumptions: @@ -314,5 +359,90 @@ void icmpv6_notify(net_ipv6addr_t ipaddr); int icmpv6_autoconfig(FAR struct net_driver_s *dev); #endif +/**************************************************************************** + * Function: icmpv6_rwait_setup + * + * Description: + * Called BEFORE an Router Solicitation is sent. This function sets up + * the Router Advertisement timeout before the Router Solicitation + * is sent so that there is no race condition when icmpv6_rwait() is + * called. + * + * Assumptions: + * This function is called from icmpv6_autoconfig() and executes in the + * normal tasking environment. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +void icmpv6_rwait_setup(FAR struct net_driver_s *dev, + FAR struct icmpv6_rnotify_s *notify); +#else +# define icmpv6_rwait_setup(d,n) +#endif + +/**************************************************************************** + * Function: icmpv6_rwait_cancel + * + * Description: + * Cancel any wait set after icmpv6_rwait_setup() is called but before + * icmpv6_rwait()is called (icmpv6_rwait() will automatically cancel the + * wait). + * + * Assumptions: + * This function may execute in the interrupt context when called from + * icmpv6_rwait(). + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +int icmpv6_rwait_cancel(FAR struct icmpv6_rnotify_s *notify); +#else +# define icmpv6_rwait_cancel(n) (0) +#endif + +/**************************************************************************** + * Function: icmpv6_rwait + * + * Description: + * Called each time that a Router Solicitation is sent. This function + * will sleep until either: (1) the matching Router Advertisement is + * received, or (2) a timeout occurs. + * + * Assumptions: + * This function is called from icmpv6_autoconfig() and must execute with + * the network un-locked (interrupts may be disabled to keep the things + * stable). + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +int icmpv6_rwait(FAR struct icmpv6_rnotify_s *notify, + FAR struct timespec *timeout); +#else +# define icmpv6_rwait(n,t) (0) +#endif + +/**************************************************************************** + * Function: icmpv6_rnotify + * + * Description: + * Called each time that a Router Advertisement is received in order to + * wake-up any threads that may be waiting for this particular Router + * Advertisement. + * + * Assumptions: + * This function is called from the MAC device driver indirectly through + * icmpv6_icmpv6in() will execute with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF +void icmpv6_rnotify(FAR struct net_driver_s *dev, const net_ipv6addr_t prefix, + unsigned int preflen); +#else +# define icmpv6_rnotify(d,p,l) +#endif + #endif /* CONFIG_NET_ICMPv6 */ #endif /* __NET_ICMPv6_ICMPv6_H */ diff --git a/net/icmpv6/icmpv6_autoconfig.c b/net/icmpv6/icmpv6_autoconfig.c index e98a46d746..ed7577d086 100644 --- a/net/icmpv6/icmpv6_autoconfig.c +++ b/net/icmpv6/icmpv6_autoconfig.c @@ -40,10 +40,257 @@ #include #ifdef CONFIG_NET_ICMPv6_AUTOCONF +#include +#include +#include #include +#include +#include + +#include +#include + +#include "devif/devif.h" #include "icmpv6/icmpv6.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CONFIG_ICMPv6_AUTOCONF_DELAYSEC \ + (CONFIG_ICMPv6_AUTOCONF_DELAYMSEC / 1000) +#define CONFIG_ICMPv6_AUTOCONF_DELAYNSEC \ + ((CONFIG_ICMPv6_AUTOCONF_DELAYMSEC - 1000*CONFIG_ICMPv6_AUTOCONF_DELAYSEC) * 1000000) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure holds the state of the send operation until it can be + * operated upon from the interrupt level. + */ + +struct icmpv6_router_s +{ + FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */ + sem_t snd_sem; /* Used to wake up the waiting thread */ + volatile bool snd_sent; /* True: if request sent */ +#ifdef CONFIG_NETDEV_MULTINIC + uint8_t snd_ifname[IFNAMSIZ]; /* Interface name */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_router_interrupt + ****************************************************************************/ + +static uint16_t icmpv6_router_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *priv, uint16_t flags) +{ + FAR struct icmpv6_router_s *state = (FAR struct icmpv6_router_s *)priv; + + nllvdbg("flags: %04x sent: %d\n", flags, state->snd_sent); + + if (state) + { +#ifdef CONFIG_NETDEV_MULTINIC + /* Is this the device that we need to route this request? */ + + if (strncmp((FAR const char *)dev->d_ifname, + (FAR const char *)state->snd_ifname, IFNAMSIZ) != 0) + { + /* No... pass on this one and wait for the device that we want */ + + return flags; + } + +#endif + + /* Check if the outgoing packet is available. It may have been claimed + * by a send interrupt serving a different thread -OR- if the output + * buffer currently contains unprocessed incoming data. In these cases + * we will just have to wait for the next polling cycle. + */ + + if (dev->d_sndlen > 0 || (flags & ICMPv6_NEWDATA) != 0) + { + /* Another thread has beat us sending data or the buffer is busy, + * Check for a timeout. If not timed out, wait for the next + * polling cycle and check again. + */ + + /* REVISIT: No timeout. Just wait for the next polling cycle */ + + return flags; + } + + /* It looks like we are good to send the data */ + /* Copy the packet data into the device packet buffer and send it */ + + icmpv6_rsolicit(dev); + + /* Make sure no additional Router Solicitation overwrites this one. + * This flag will be cleared in icmpv6_out(). + */ + + IFF_SET_NOARP(dev->d_flags); + + /* Don't allow any further call backs. */ + + state->snd_sent = true; + state->snd_cb->flags = 0; + state->snd_cb->priv = NULL; + state->snd_cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&state->snd_sem); + } + + return flags; +} + +/**************************************************************************** + * Name: icmpv6_send_rsolicit + * + * Description: + * Send an ICMPv6 Router Solicitation to resolve an IPv6 address. + * + * Parameters: + * dev - The device to use to send the solicitation + * + * Returned Value: + * Zero (OK) is returned on success; On error a negated errno value is + * returned. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +int icmpv6_send_rsolicit(FAR struct net_driver_s *dev) +{ + struct icmpv6_router_s state; + int ret; + + /* Initialize the state structure. This is done with interrupts + * disabled + */ + + (void)sem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */ + +#ifdef CONFIG_NETDEV_MULTINIC + /* Remember the routing device name */ + + strncpy((FAR char *)state.snd_ifname, (FAR const char *)dev->d_ifname, + IFNAMSIZ); +#endif + + /* Allocate resources to receive a callback. This and the following + * initialization is performed with the network lock because we don't + * want anything to happen until we are ready. + */ + + state.snd_cb = icmpv6_callback_alloc(); + if (!state.snd_cb) + { + ndbg("ERROR: Failed to allocate a cllback\n"); + ret = -ENOMEM; + goto errout_with_semaphore; + } + + /* Arm the callback */ + + state.snd_sent = false; + state.snd_cb->flags = ICMPv6_POLL; + state.snd_cb->priv = (FAR void *)&state; + state.snd_cb->event = icmpv6_router_interrupt; + + /* Notify the device driver that new TX data is available. */ + + dev->d_txavail(dev); + + /* Wait for the send to complete or an error to occur: NOTES: (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. + */ + + do + { + (void)net_lockedwait(&state.snd_sem); + } + while (!state.snd_sent); + + icmpv6_callback_free(state.snd_cb); + +errout_with_semaphore: + sem_destroy(&state.snd_sem); + return ret; +} + +/**************************************************************************** + * Name: icmpv6_wait_radvertise + * + * Description: + * Wait for the receipt of the Router Advertisment matching the Router + * Solicitation that we just sent. + * + * Parameters: + * dev - The device to use to send the solicitation + * notify - The pre-initialized notification structure + * save - We will need this to temporarily release the net lock + * + * Returned Value: + * Zero (OK) is returned on success; On error a negated errno value is + * returned. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +int icmpv6_wait_radvertise(FAR struct net_driver_s *dev, + FAR struct icmpv6_rnotify_s *notify, + net_lock_t *save) +{ + struct timespec delay; +#ifdef CONFIG_NET_NOINTS + irqstate_t flags; +#endif + int ret; + + /* Wait for response to the Router Advertisement to be received. The + * optimal delay would be the work case round trip time. + * NOTE: The network is locked. + */ + + delay.tv_sec = CONFIG_ICMPv6_AUTOCONF_DELAYSEC; + delay.tv_nsec = CONFIG_ICMPv6_AUTOCONF_DELAYNSEC; + +#ifdef CONFIG_NET_NOINTS + flags = irqsave(); /* Keep things stable */ + net_unlock(*save); /* Unlock the network with interrupts disabled */ +#endif + ret = icmpv6_rwait(notify, &delay); +#ifdef CONFIG_NET_NOINTS + *save = net_lock(); /* Re-lock the network with interrupts disabled */ + irqrestore(flags); +#endif + + /* icmpv6_wait will return OK if and only if the matching Router + * Advertisement is received. Otherwise, it will return -ETIMEDOUT. + */ + + return ret; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -74,7 +321,36 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev) { +#ifndef CONFIG_NET_ETHERNET + /* Only Ethernet supported for now */ + + ndbg("ERROR: Only Ethernet is supported\n"); + return -ENOSYS; + +#else + struct icmpv6_rnotify_s notify; + net_ipv6addr_t lladdr; + net_lock_t save; + int retries; + int ret; + + /* Sanity checks */ + + DEBUGASSERT(dev); + nvdbg("Auto-configuring %s\n", dev->d_ifname); + +#ifdef CONFIG_NET_MULTILINK + /* Only Ethernet devices are supported for now */ + + if (dev->d_lltype != NET_LL_ETHERNET) + { + ndbg("ERROR: Only Ethernet is supported\n"); + return -ENOSYS; + } +#endif + /* IPv6 Stateless Autoconfiguration + * Reference: http://www.tcpipguide.com/free/t_IPv6AutoconfiguratinoandRenumbering.htm * * The following is a summary of the steps a device takes when using * stateless auto-configuration: @@ -93,7 +369,17 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev) * simply take the EUI-64 address and change the 7th bit from the left * (the"universal/local" or "U/L" bit) from a zero to a one. * - * 2. Link-Local Address Uniqueness Test: The node tests to ensure that + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 xxxx xxxx xxxx + */ + + lladdr[0] = 0xfe80; /* 10-bit address + 6 zeroes */ + memset(&lladdr[1], 0, 4* sizeof(uint16_t)); /* 64 more zeroes */ + memcpy(&lladdr[5], dev->d_mac.ether_addr_octet, sizeof(struct ether_addr)); /* 48-bit Ethernet address */ + +#ifdef CONFIG_NET_ICMPv6_NEIGHBOR + /* 2. Link-Local Address Uniqueness Test: The node tests to ensure that * the address it generated isn't for some reason already in use on the * local network. (This is very unlikely to be an issue if the link-local * address came from a MAC address but more likely if it was based on a @@ -103,36 +389,97 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev) * already using its link-local address; if so, either a new address * must be generated, or auto-configuration fails and another method * must be employed. - * - * 3. Link-Local Address Assignment: Assuming the uniqueness test passes, + */ + + ret = icmpv6_neighbor(lladdr); + if (ret == OK) + { + /* Hmmm... someone else responded to our Neighbor Solicitation. We + * have not back-up plan in place. Just bail. + */ + + return -EEXIST; + } +#endif + + /* 3. Link-Local Address Assignment: Assuming the uniqueness test passes, * the device assigns the link-local address to its IP interface. This * address can be used for communication on the local network, but not * on the wider Internet (since link-local addresses are not routed). - * - * 4. Router Contact: The node next attempts to contact a local router for + */ + + net_ipv6addr_copy(dev->d_ipv6addr, lladdr); + + /* 4. Router Contact: The node next attempts to contact a local router for * more information on continuing the configuration. This is done either * by listening for Router Advertisement messages sent periodically by * routers, or by sending a specific Router Solicitation to ask a router * for information on what to do next. - * - * 5. Router Direction: The router provides direction to the node on how to + */ + + save = net_lock(); + for (retries = 0; retries < CONFIG_ICMPv6_AUTOCONF_MAXTRIES; retries++) + { + /* Set up the Router Advertisement BEFORE we send the Router + * Solicitation. + */ + + icmpv6_rwait_setup(dev, ¬ify); + + /* Send the ICMPv6 Router solicitation message */ + + ret = icmpv6_send_rsolicit(dev); + if (ret < 0) + { + ndbg("ERROR: Failed send router solicitation: %d\n", ret); + break; + } + + /* Wait to receive the Router Advertisement message */ + + ret = icmpv6_wait_radvertise(dev, ¬ify, &save); + if (ret != -ETIMEDOUT) + { + /* ETIMEDOUT is the only expected failure. We will retry on that + * case only. + */ + + break; + } + + nvdbg("Timed out... retrying\n"); + } + + net_unlock(save); + + /* Check for failures */ + + if (ret < 0) + { + ndbg("ERROR: Failed to get the router advertisement: %d (retries=%d)\n", + ret, retries); + return ret; + } + + /* 5. Router Direction: The router provides direction to the node on how to * proceed with the auto-configuration. It may tell the node that on this * network "stateful" auto-configuration is in use, and tell it the * address of a DHCP server to use. Alternately, it will tell the host * how to determine its global Internet address. - * - * 6. Global Address Configuration: Assuming that stateless auto- + */ +#warning Missing logic + + /* 6. Global Address Configuration: Assuming that stateless auto- * configuration is in use on the network, the host will configure * itself with its globally-unique Internet address. This address is * generally formed from a network prefix provided to the host by the * router, combined with the device's identifier as generated in the * first step. - * - * Reference: http://www.tcpipguide.com/free/t_IPv6AutoconfiguratinoandRenumbering.htm */ - #warning Missing logic - return -ENOSYS; + + return ret; +#endif } #endif /* CONFIG_NET_ICMPv6_AUTOCONF */ diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index b136e7009a..e3dc4d24b4 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -75,6 +75,8 @@ ((struct icmpv6_neighbor_solicit_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) #define ICMPv6ADVERTISE \ ((struct icmpv6_neighbor_advertise_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +#define ICMPv6RADVERTISE \ + ((struct icmpv6_router_advertise_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) /**************************************************************************** * Public Data @@ -116,206 +118,272 @@ void icmpv6_input(FAR struct net_driver_s *dev) g_netstats.icmpv6.recv++; #endif - /* If we get a neighbor solicitation for our address we should send - * a neighbor advertisement message back. - */ + /* Handle the ICMPv6 message by its type */ - if (icmp->type == ICMPv6_NEIGHBOR_SOLICIT) + switch (icmp->type) { - FAR struct icmpv6_neighbor_solicit_s *sol; - FAR struct icmpv6_neighbor_advertise_s *adv; + /* If we get a neighbor solicitation for our address we should send + * a neighbor advertisement message back. + */ - /* Check if we are the target of the solicitation */ + case ICMPv6_NEIGHBOR_SOLICIT: + { + FAR struct icmpv6_neighbor_solicit_s *sol; + FAR struct icmpv6_neighbor_advertise_s *adv; - sol = ICMPv6SOLICIT; - if (net_ipv6addr_cmp(sol->tgtaddr, dev->d_ipv6addr)) - { - /* Yes.. Send a neighbor advertisement back to where the neighbor - * solicitation came from. - * - * - * Set up the IPv6 header. Most is probably already in place from - * the Neighbor Solicitation. We could save some time here. - */ + /* Check if we are the target of the solicitation */ - icmp->vtc = 0x60; /* Version/traffic class (MS) */ - icmp->tcf = 0; /* Traffic class (LS)/Flow label (MS) */ - icmp->flow = 0; /* Flow label (LS) */ + sol = ICMPv6SOLICIT; + if (net_ipv6addr_cmp(sol->tgtaddr, dev->d_ipv6addr)) + { + /* Yes.. Send a neighbor advertisement back to where the neighbor + * solicitation came from. + * + * Set up the IPv6 header. Most is probably already in place from + * the Neighbor Solicitation. We could save some time here. + */ - /* Length excludes the IPv6 header */ + icmp->vtc = 0x60; /* Version/traffic class (MS) */ + icmp->tcf = 0; /* Traffic class (LS)/Flow label (MS) */ + icmp->flow = 0; /* Flow label (LS) */ - icmp->len[0] = (sizeof(struct icmpv6_neighbor_advertise_s) >> 8); - icmp->len[1] = (sizeof(struct icmpv6_neighbor_advertise_s) & 0xff); + /* Length excludes the IPv6 header */ - icmp->proto = IP_PROTO_ICMP6; /* Next header */ - icmp->ttl = 255; /* Hop limit */ + icmp->len[0] = (sizeof(struct icmpv6_neighbor_advertise_s) >> 8); + icmp->len[1] = (sizeof(struct icmpv6_neighbor_advertise_s) & 0xff); - /* Swap source for destination IP address, add our source IP - * address - */ + icmp->proto = IP_PROTO_ICMP6; /* Next header */ + icmp->ttl = 255; /* Hop limit */ - net_ipv6addr_copy(icmp->destipaddr, icmp->srcipaddr); - net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + /* Swap source for destination IP address, add our source IP + * address + */ - /* Set up the ICMPv6 Neighbor Advertise response */ + net_ipv6addr_copy(icmp->destipaddr, icmp->srcipaddr); + net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); - adv = ICMPv6ADVERTISE; - adv->type = ICMPv6_NEIGHBOR_ADVERTISE; /* Message type */ - adv->code = 0; /* Message qualifier */ - adv->flags[0] = ICMPv6_FLAG_S | ICMPv6_FLAG_O; /* Solicited+Override flags. */ - adv->flags[1] = 0; - adv->flags[2] = 0; - adv->flags[3] = 0; + /* Set up the ICMPv6 Neighbor Advertise response */ - /* Copy the target address into the Neighbor Advertisement message */ + adv = ICMPv6ADVERTISE; + adv->type = ICMPv6_NEIGHBOR_ADVERTISE; /* Message type */ + adv->code = 0; /* Message qualifier */ + adv->flags[0] = ICMPv6_NADV_FLAG_S | ICMPv6_NADV_FLAG_O; /* Solicited+Override flags. */ + adv->flags[1] = 0; + adv->flags[2] = 0; + adv->flags[3] = 0; - net_ipv6addr_copy(adv->tgtaddr, dev->d_ipv6addr); + /* Copy the target address into the Neighbor Advertisement message */ - /* Set up the options */ + net_ipv6addr_copy(adv->tgtaddr, dev->d_ipv6addr); - adv->opttype = ICMPv6_OPT_TGTLLADDR; /* Option type */ - adv->optlen = 1; /* Option length = 1 octet */ + /* Set up the options */ - /* Copy our link layer address into the message - * REVISIT: What if the link layer is not Ethernet? - */ + adv->opttype = ICMPv6_OPT_TGTLLADDR; /* Option type */ + adv->optlen = 1; /* Option length = 1 octet */ - memcpy(adv->tgtlladdr, &dev->d_mac, IFHWADDRLEN); + /* Copy our link layer address into the message + * REVISIT: What if the link layer is not Ethernet? + */ - /* Calculate the checksum over both the ICMP header and payload */ + memcpy(adv->tgtlladdr, &dev->d_mac, IFHWADDRLEN); - icmp->chksum = 0; - icmp->chksum = ~icmpv6_chksum(dev); + /* Calculate the checksum over both the ICMP header and payload */ - /* Set the size to the size of the IPv6 header and the payload size */ + icmp->chksum = 0; + icmp->chksum = ~icmpv6_chksum(dev); - dev->d_len = IPv6_HDRLEN + sizeof(struct icmpv6_neighbor_advertise_s); + /* Set the size to the size of the IPv6 header and the payload size */ + + dev->d_len = IPv6_HDRLEN + sizeof(struct icmpv6_neighbor_advertise_s); #ifdef CONFIG_NET_ETHERNET - /* Add the size of the Ethernet header */ + /* Add the size of the Ethernet header */ - dev->d_len += ETH_HDRLEN; + dev->d_len += ETH_HDRLEN; - /* Move the source and to the destination addresses in the - * Ethernet header and use our MAC as the new source address. - */ + /* Move the source and to the destination addresses in the + * Ethernet header and use our MAC as the new source address. + */ #ifdef CONFIG_NET_MULTILINK - if (dev->d_lltype == NET_LL_ETHERNET) + if (dev->d_lltype == NET_LL_ETHERNET) #endif - { - FAR struct eth_hdr_s *eth = ETHBUF; + { + FAR struct eth_hdr_s *eth = ETHBUF; - memcpy(eth->dest, eth->src, ETHER_ADDR_LEN); - memcpy(eth->src, dev->d_mac.ether_addr_octet, ETHER_ADDR_LEN); + memcpy(eth->dest, eth->src, ETHER_ADDR_LEN); + memcpy(eth->src, dev->d_mac.ether_addr_octet, ETHER_ADDR_LEN); - /* Set the IPv6 Ethernet type */ + /* Set the IPv6 Ethernet type */ - eth->type = HTONS(ETHTYPE_IP6); - } + eth->type = HTONS(ETHTYPE_IP6); + } #endif - /* No additional neighbor lookup is required on this packet - * (We are using a multicast address). - */ - IFF_SET_NOARP(dev->d_flags); - } - else - { - goto icmpv6_drop_packet; - } - } + /* No additional neighbor lookup is required on this packet + * (We are using a multicast address). + */ - /* If we get a neighbor advertise for our address we should send - * a neighbor advertisement message back. - */ + IFF_SET_NOARP(dev->d_flags); + } + else + { + goto icmpv6_drop_packet; + } + } + break; - else if (icmp->type == ICMPv6_NEIGHBOR_ADVERTISE) - { - FAR struct icmpv6_neighbor_advertise_s *adv; + /* Check if we received a Neighbor Advertisement */ - /* If the IPv6 destination address matches our address, and if so, - * add the neighbor address mapping to the list of neighbors. - * - * Missing checks: - * optlen = 1 (8 octets) - * Should only update Neighbor Table if [O]verride bit is set in flags - */ + case ICMPv6_NEIGHBOR_ADVERTISE: + { + FAR struct icmpv6_neighbor_advertise_s *adv; - adv = ICMPv6ADVERTISE; - if (net_ipv6addr_cmp(icmp->destipaddr, dev->d_ipv6addr)) - { - /* This message is required to support the Target link-layer - * address option. - */ + /* If the IPv6 destination address matches our address, and if so, + * add the neighbor address mapping to the list of neighbors. + * + * Missing checks: + * optlen = 1 (8 octets) + * Should only update Neighbor Table if [O]verride bit is set in flags + */ - if (adv->opttype == ICMPv6_OPT_TGTLLADDR) - { - /* Save the sender's address mapping in our Neighbor Table. */ + adv = ICMPv6ADVERTISE; + if (net_ipv6addr_cmp(icmp->destipaddr, dev->d_ipv6addr)) + { + /* This message is required to support the Target link-layer + * address option. + */ - neighbor_add(icmp->srcipaddr, - (FAR struct neighbor_addr_s *)adv->tgtlladdr); + if (adv->opttype == ICMPv6_OPT_TGTLLADDR) + { + /* Save the sender's address mapping in our Neighbor Table. */ + + neighbor_add(icmp->srcipaddr, + (FAR struct neighbor_addr_s *)adv->tgtlladdr); #ifdef CONFIG_NET_ICMPv6_NEIGHBOR - /* Then notify any logic waiting for the Neighbor Advertisement */ + /* Then notify any logic waiting for the Neighbor Advertisement */ - icmpv6_notify(icmp->srcipaddr); + icmpv6_notify(icmp->srcipaddr); #endif - /* We consumed the packet but we don't send anything in - * response. - */ + /* We consumed the packet but we don't send anything in + * response. + */ - goto icmpv_send_nothing; - } - } + goto icmpv_send_nothing; + } + } - goto icmpv6_drop_packet; - } - else if (icmp->type == ICMPv6_ECHO_REQUEST) - { - /* ICMPv6 echo (i.e., ping) processing. This is simple, we only - * change the ICMPv6 type from ECHO to ECHO_REPLY and update the - * ICMPv6 checksum before we return the packet. - */ + goto icmpv6_drop_packet; + } + break; - icmp->type = ICMPv6_ECHO_REPLY; +#ifdef CONFIG_NET_ICMPv6_AUTOCONF + /* Check if we received a Router Advertisement */ - net_ipv6addr_copy(icmp->destipaddr, icmp->srcipaddr); - net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + case ICMPV6_ROUTER_ADVERTISE: + { + FAR struct icmpv6_router_advertise_s *adv; + uint16_t pktlen; + uint16_t optlen; + int ndx; - icmp->chksum = 0; - icmp->chksum = ~icmpv6_chksum(dev); - } + /* Get the length of the option data */ + + pktlen = (uint16_t)icmp->len[0] << 8 | icmp->len[1]; + if (pktlen <= ICMPv6_RADV_MINLEN) + { + /* Too small to contain any options */ + + goto icmpv6_drop_packet; + } + + optlen = ICMPv6_RADV_OPTLEN(pktlen); + + /* We need to have a valid router advertisement with a Prefix and + * with the "A" bit set in the flags. + */ + + adv = ICMPv6RADVERTISE; + for (ndx = 0; ndx + sizeof(struct icmpv6_prefixinfo_s) < optlen; ) + { + FAR struct icmpv6_prefixinfo_s *opt = + (FAR struct icmpv6_prefixinfo_s *)&adv->options[ndx]; + + /* Is this the sought for prefix? Is it the correct size? Is + * the "A" flag set? + */ + + if (opt->opttype && + opt->optlen == 4 && + (opt->flags & ICMPv6_PRFX_FLAG_A) != 0) + { + /* Yes.. Notify any waiting threads */ + + icmpv6_rnotify(dev, opt->prefix, opt->preflen); + goto icmpv_send_nothing; + } + + /* Skip to the next option (units of octets) */ + + ndx += (opt->optlen << 3); + } + + goto icmpv6_drop_packet; + } + break; +#endif + + /* Handle the ICMPv6 Echo Request */ + + case ICMPv6_ECHO_REQUEST: + { + /* ICMPv6 echo (i.e., ping) processing. This is simple, we only + * change the ICMPv6 type from ECHO to ECHO_REPLY and update the + * ICMPv6 checksum before we return the packet. + */ + + icmp->type = ICMPv6_ECHO_REPLY; + + net_ipv6addr_copy(icmp->destipaddr, icmp->srcipaddr); + net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + + icmp->chksum = 0; + icmp->chksum = ~icmpv6_chksum(dev); + } + break; #ifdef CONFIG_NET_ICMPv6_PING - /* If an ICMPv6 echo reply is received then there should also be - * a thread waiting to received the echo response. - */ + /* If an ICMPv6 echo reply is received then there should also be + * a thread waiting to received the echo response. + */ - else if (icmp->type == ICMPv6_ECHO_REPLY) - { - uint16_t flags = ICMPv6_ECHOREPLY; + case ICMPv6_ECHO_REPLY: + { + uint16_t flags = ICMPv6_ECHOREPLY; - /* Dispatch the ECHO reply to the waiting thread */ + /* Dispatch the ECHO reply to the waiting thread */ - flags = devif_callback_execute(dev, icmp, flags, g_icmpv6_conn.list); + flags = devif_callback_execute(dev, icmp, flags, g_icmpv6_conn.list); - /* If the ECHO reply was not handled, then drop the packet */ + /* If the ECHO reply was not handled, then drop the packet */ - if (flags == ICMPv6_ECHOREPLY) - { - /* The ECHO reply was not handled */ + if (flags == ICMPv6_ECHOREPLY) + { + /* The ECHO reply was not handled */ - goto icmpv6_drop_packet; - } - } + goto icmpv6_drop_packet; + } + } + break; #endif - else - { - nlldbg("Unknown ICMPv6 cmd: %d\n", icmp->type); - goto icmpv6_type_error; + default: + { + nlldbg("Unknown ICMPv6 cmd: %d\n", icmp->type); + goto icmpv6_type_error; + } } nllvdbg("Outgoing ICMPv6 packet length: %d (%d)\n", diff --git a/net/icmpv6/icmpv6_neighbor.c b/net/icmpv6/icmpv6_neighbor.c index aa916ac088..8c5d96180b 100644 --- a/net/icmpv6/icmpv6_neighbor.c +++ b/net/icmpv6/icmpv6_neighbor.c @@ -357,11 +357,7 @@ int icmpv6_neighbor(const net_ipv6addr_t ipaddr) state.snd_cb->priv = (FAR void *)&state; state.snd_cb->event = icmpv6_neighbor_interrupt; - /* Notify the device driver that new TX data is available. - * NOTES: This is in essence what netdev_ipv4_txnotify() does, which - * is not possible to call since it expects a icmpv6_neighbor as - * its single argument to lookup the network interface. - */ + /* Notify the device driver that new TX data is available. */ dev->d_txavail(dev); diff --git a/net/icmpv6/icmpv6_notify.c b/net/icmpv6/icmpv6_notify.c index a5c0bd3077..692b9206c1 100644 --- a/net/icmpv6/icmpv6_notify.c +++ b/net/icmpv6/icmpv6_notify.c @@ -82,7 +82,7 @@ static struct icmpv6_notify_s *g_icmpv6_waiters; * * Description: * Called BEFORE an Neighbor Solicitation is sent. This function sets up - * the Neighbor Advertisement timeout before the the Neighbor Solicitation + * the Neighbor Advertisement timeout before the Neighbor Solicitation * is sent so that there is no race condition when icmpv6_wait() is called. * * Assumptions: @@ -243,7 +243,7 @@ void icmpv6_notify(net_ipv6addr_t ipaddr) * entry from the list. */ - if (curr->nt_result != OK && curr->nt_ipaddr == ipaddr) + if (curr->nt_result != OK && net_ipv6addr_cmp(curr->nt_ipaddr, ipaddr)) { /* Yes.. Signal the waiting, returning success */ diff --git a/net/icmpv6/icmpv6_rsolicit.c b/net/icmpv6/icmpv6_rsolicit.c new file mode 100644 index 0000000000..132c3e0fc0 --- /dev/null +++ b/net/icmpv6/icmpv6_rsolicit.c @@ -0,0 +1,218 @@ +/**************************************************************************** + * net/icmpv6/icmpv6_rsolicit.c + * + * Copyright (C) 2015 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 "devif/devif.h" +#include "utils/utils.h" +#include "icmpv6/icmpv6.h" + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF + +/**************************************************************************** + * 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 ICMPv6RSOLICIT \ + ((struct icmpv6_router_solicit_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_rsolicit + * + * Description: + * Set up to send an ICMPv6 Router Solicitation message. This version + * is for a standalone solicitation. If formats: + * + * - The Ethernet header + * - The IPv6 header + * - The ICMPv6 Router Solicitation Message + * + * The device IP address should have been set to the link local address + * prior to calling this function. + * + * Parameters: + * dev - Reference to an Ethernet device driver structure + * + * Return: + * None + * + ****************************************************************************/ + +void icmpv6_rsolicit(FAR struct net_driver_s *dev) +{ + FAR struct icmpv6_iphdr_s *icmp; + FAR struct icmpv6_router_solicit_s *sol; + FAR struct eth_hdr_s *eth; + + /* 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 */ + + icmp->len[0] = (sizeof(struct icmpv6_router_solicit_s) >> 8); + icmp->len[1] = (sizeof(struct icmpv6_router_solicit_s) & 0xff); + + icmp->proto = IP_PROTO_ICMP6; /* Next header */ + icmp->ttl = 255; /* Hop limit */ + + /* Set the multicast destination IP address to the IPv7 all routers + * address: ff02::2 + */ + + icmp->destipaddr[0] = 0xff02; + memset(&icmp->destipaddr[0], 0, 6*sizeof(uint16_t)); + icmp->destipaddr[7] = 0x0002; + + /* Add our link local IPv6 address as the source address */ + + net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + + /* Set up the ICMPv6 Neighbor Solicitation message */ + + sol = ICMPv6RSOLICIT; + sol->type = ICMPV6_ROUTER_SOLICIT; /* Message type */ + sol->code = 0; /* Message qualifier */ + sol->flags[0] = 0; /* flags */ + sol->flags[1] = 0; + sol->flags[2] = 0; + sol->flags[3] = 0; + + /* Set up the options */ + + sol->opttype = ICMPv6_OPT_SRCLLADDR; /* Option type */ + sol->optlen = 1; /* Option length = 1 octet */ + + /* Copy our link layer address into the message + * REVISIT: What if the link layer is not Ethernet? + */ + + memcpy(sol->srclladdr, &dev->d_mac, IFHWADDRLEN); + + /* 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 */ + + dev->d_len = IPv6_HDRLEN + sizeof(struct icmpv6_router_solicit_s); + +#ifdef CONFIG_NET_ETHERNET +#ifdef CONFIG_NET_MULTILINK + if (dev->d_lltype == NET_LL_ETHERNET) +#endif + { + /* Set the destination IPv6 multicast Ethernet address: + * + * For IPv6 multicast addresses, the Ethernet MAC is derived by + * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00, + * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map + * to the Ethernet MAC address 33:33:00:01:00:03. + * + * NOTES: This appears correct for the ICMPv6 Router Solicitation + * Message, but the ICMPv6 Neighbor Solicitation message seems to + * use 33:33:ff:01:00:03. + */ + + eth = ETHBUF; + eth->dest[0] = 0x33; + eth->dest[1] = 0x33; + eth->dest[2] = 0xff; + eth->dest[3] = dev->d_ipv6addr[6] >> 8; + eth->dest[4] = dev->d_ipv6addr[7] & 0xff; + eth->dest[5] = dev->d_ipv6addr[7] >> 8; + + /* Move our source Ethernet addresses into the Ethernet header */ + + memcpy(eth->src, dev->d_mac.ether_addr_octet, ETHER_ADDR_LEN); + + /* Set the IPv6 Ethernet type */ + + eth->type = HTONS(ETHTYPE_IP6); +#if 0 + /* No additional neighbor lookup is required on this packet. + * REVISIT: It is inappropriate to set this bit if we get here + * via neighbor_out(); It is no necessary to set this bit if we + * get here via icmpv6_input(). Is it ever necessary? + */ + + IFF_SET_NOARP(dev->d_flags); +#endif + } +#endif + + /* Add the size of the layer layer header to the total size of the + * outgoing packet. + */ + +#if defined(CONFIG_NET_MULTILINK) + dev->d_len += dev->d_llhdrlen; +#elif defined(CONFIG_NET_ETHERNET) + dev->d_len += ETH_HDRLEN; +#else /* if defined(CONFIG_NET_SLIP) */ + /* SLIP has no link layer header */ +#endif + + nllvdbg("Outgoing ICMPv6 Neighbor Solicitation 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 +} + +#endif /* CONFIG_NET_ICMPv6_AUTOCONF */