Networking: Add support for sending ICMPv6 Router Solicitation and receiving Router advertisement. This is a part of the ICMPv6 auto-configuration logic is still still not complete

This commit is contained in:
Gregory Nutt 2015-02-03 13:25:28 -06:00
parent 59d5149de6
commit 66f0198e3f
11 changed files with 1059 additions and 176 deletions

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -40,10 +40,257 @@
#include <nuttx/config.h>
#ifdef CONFIG_NET_ICMPv6_AUTOCONF
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <debug.h>
#include <net/ethernet.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#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, &notify);
/* 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, &notify, &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 */

View File

@ -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",

View File

@ -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);

View File

@ -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 */

View File

@ -0,0 +1,218 @@
/****************************************************************************
* net/icmpv6/icmpv6_rsolicit.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include <debug.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#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 */