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:
parent
59d5149de6
commit
66f0198e3f
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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, ¬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 */
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
218
net/icmpv6/icmpv6_rsolicit.c
Normal file
218
net/icmpv6/icmpv6_rsolicit.c
Normal 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 */
|
Loading…
Reference in New Issue
Block a user