From 7edab99a2a81d2f6c522e6b1b889321cc7f8c030 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 23 Jan 2015 14:06:08 -0600 Subject: Networking: First cut at ICMPv6 ping logic --- nuttx/include/nuttx/net/icmpv6.h | 30 +++++++ nuttx/net/devif/devif.h | 142 +++++++++++++++---------------- nuttx/net/icmp/icmp_ping.c | 2 +- nuttx/net/icmpv6/icmpv6_ping.c | 170 +++++++++++++++++++++++++++----------- nuttx/net/icmpv6/icmpv6_solicit.c | 7 +- 5 files changed, 233 insertions(+), 118 deletions(-) diff --git a/nuttx/include/nuttx/net/icmpv6.h b/nuttx/include/nuttx/net/icmpv6.h index 108f1c19b..20f922396 100644 --- a/nuttx/include/nuttx/net/icmpv6.h +++ b/nuttx/include/nuttx/net/icmpv6.h @@ -175,6 +175,36 @@ struct icmpv6_neighbor_advertise_s #endif }; +/* This the message format for the ICMPv6 Echo Request message */ + +struct icmpv6_echo_request_s +{ + uint8_t type; /* Message Type: ICMPv6_ECHO_REQUEST */ + uint8_t code; /* Further qualifies the ICMP messages */ + uint16_t chksum; /* Checksum of ICMP header and data */ + uint16_t id; /* Identifier */ + uint16_t seqno; /* Sequence Number */ + uint8_t data[1]; /* Data follows */ +}; + +#define SIZEOF_ICMPV6_ECHO_REQUEST_S(n) \ + (sizeof(struct icmpv6_echo_request_s) - 1 + (n)) + +/* This the message format for the ICMPv6 Echo Reply message */ + +struct icmpv6_echo_reply_s +{ + uint8_t type; /* Message Type: ICMPv6_ECHO_REQUEST */ + uint8_t code; /* Further qualifies the ICMP messages */ + uint16_t chksum; /* Checksum of ICMP header and data */ + uint16_t id; /* Identifier */ + uint16_t seqno; /* Sequence Number */ + uint8_t data[1]; /* Data follows */ +}; + +#define SIZEOF_ICMPV6_ECHO_REPLY_S(n) \ + (sizeof(struct icmpv6_echo_reply_s) - 1 + (n)) + /* The structure holding the ICMP statistics that are gathered if * CONFIG_NET_STATISTICS is defined. */ diff --git a/nuttx/net/devif/devif.h b/nuttx/net/devif/devif.h index 06ce8cc06..ca40f1336 100644 --- a/nuttx/net/devif/devif.h +++ b/nuttx/net/devif/devif.h @@ -61,78 +61,80 @@ * TCP_ACKDATA, XYZ_NEWDATA, and TCP_CLOSE flags may be set at the same time, * whereas the others are mutually exclusive. * - * TCP_ACKDATA IN: Signifies that the outstanding data was ACKed and - * the socket layer should send out new data instead - * of retransmitting the last data (TCP only) - * OUT: Input state must be preserved on output. - * - * TCP_NEWDATA IN: Set to indicate that the peer has sent us new data. - * 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. - * - * TCP_SNDACK IN: Not used; always zero - * OUT: Set by the socket layer if the new data was consumed - * and an ACK should be sent in the response. (TCP only) - * - * TCP_REXMIT IN: Tells the socket layer to retransmit the data that - * was last sent. (TCP only) - * OUT: Not used - * - * TCP_POLL IN: Used for polling the socket layer. This is provided - * UDP_POLL periodically from the drivers to support (1) timed - * PKT_POLL operations, and (2) to check if the socket layer has - * ICMP_POLL data that it wants to send - * ICMPv6_POLL OUT: Not used - * - * TCP_BACKLOG IN: There is a new connection in the backlog list set - * up by the listen() command. (TCP only) - * OUT: Not used - * - * TCP_CLOSE IN: The remote host has closed the connection, thus the - * connection has gone away. (TCP only) - * OUT: The socket layer signals that it wants to close the - * connection. (TCP only) - * - * TCP_ABORT IN: The remote host has aborted the connection, thus the - * connection has gone away. (TCP only) - * OUT: The socket layer signals that it wants to abort the - * connection. (TCP only) - * - * TCP_CONNECTED IN: We have got a connection from a remote host and have - * set up a new connection for it, or an active connection - * has been successfully established. (TCP only) - * OUT: Not used - * - * TCP_TIMEDOUT IN: The connection has been aborted due to too many - * retransmissions. (TCP only) - * OUT: Not used - * - * ICMP_ECHOREPLY IN: An ICMP Echo Reply has been received. Used to support - * ICMP ping from the socket layer. (ICMP only) - * OUT: Cleared (only) by the socket layer logic to indicate - * that the reply was processed, suppressing further - * attempts to process the reply. + * TCP_ACKDATA IN: Signifies that the outstanding data was ACKed and + * the socket layer should send out new data instead + * of retransmitting the last data (TCP only) + * OUT: Input state must be preserved on output. + * + * TCP_NEWDATA IN: Set to indicate that the peer has sent us new data. + * 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. + * + * TCP_SNDACK IN: Not used; always zero + * OUT: Set by the socket layer if the new data was consumed + * and an ACK should be sent in the response. (TCP only) + * + * TCP_REXMIT IN: Tells the socket layer to retransmit the data that + * was last sent. (TCP only) + * OUT: Not used + * + * TCP_POLL IN: Used for polling the socket layer. This is provided + * UDP_POLL periodically from the drivers to support (1) timed + * PKT_POLL operations, and (2) to check if the socket layer has + * ICMP_POLL data that it wants to send + * ICMPv6_POLL OUT: Not used + * + * TCP_BACKLOG IN: There is a new connection in the backlog list set + * up by the listen() command. (TCP only) + * OUT: Not used + * + * TCP_CLOSE IN: The remote host has closed the connection, thus the + * connection has gone away. (TCP only) + * OUT: The socket layer signals that it wants to close the + * connection. (TCP only) + * + * TCP_ABORT IN: The remote host has aborted the connection, thus the + * connection has gone away. (TCP only) + * OUT: The socket layer signals that it wants to abort the + * connection. (TCP only) + * + * TCP_CONNECTED IN: We have got a connection from a remote host and have + * set up a new connection for it, or an active connection + * has been successfully established. (TCP only) + * OUT: Not used + * + * TCP_TIMEDOUT IN: The connection has been aborted due to too many + * retransmissions. (TCP only) + * OUT: Not used + * + * ICMP_ECHOREPLY IN: An ICMP Echo Reply has been received. Used to support + * ICMPv6_ECHOREPLY ICMP ping from the socket layer. (ICMP only) + * OUT: Cleared (only) by the socket layer logic to indicate + * that the reply was processed, suppressing further + * attempts to process the reply. */ -#define TCP_ACKDATA (1 << 0) -#define TCP_NEWDATA (1 << 1) -#define UDP_NEWDATA TCP_NEWDATA -#define PKT_NEWDATA TCP_NEWDATA -#define ICMP_NEWDATA TCP_NEWDATA -#define TCP_SNDACK (1 << 2) -#define TCP_REXMIT (1 << 3) -#define TCP_POLL (1 << 4) -#define UDP_POLL TCP_POLL -#define PKT_POLL TCP_POLL -#define ICMP_POLL TCP_POLL -#define ICMPv6_POLL TCP_POLL -#define TCP_BACKLOG (1 << 5) -#define TCP_CLOSE (1 << 6) -#define TCP_ABORT (1 << 7) -#define TCP_CONNECTED (1 << 8) -#define TCP_TIMEDOUT (1 << 9) -#define ICMP_ECHOREPLY (1 << 10) +#define TCP_ACKDATA (1 << 0) +#define TCP_NEWDATA (1 << 1) +#define UDP_NEWDATA TCP_NEWDATA +#define PKT_NEWDATA TCP_NEWDATA +#define ICMP_NEWDATA TCP_NEWDATA +#define ICMPv6_NEWDATA TCP_NEWDATA +#define TCP_SNDACK (1 << 2) +#define TCP_REXMIT (1 << 3) +#define TCP_POLL (1 << 4) +#define UDP_POLL TCP_POLL +#define PKT_POLL TCP_POLL +#define ICMP_POLL TCP_POLL +#define ICMPv6_POLL TCP_POLL +#define TCP_BACKLOG (1 << 5) +#define TCP_CLOSE (1 << 6) +#define TCP_ABORT (1 << 7) +#define TCP_CONNECTED (1 << 8) +#define TCP_TIMEDOUT (1 << 9) +#define ICMP_ECHOREPLY (1 << 10) +#define ICMPv6_ECHOREPLY ICMP_ECHOREPLY #define TCP_CONN_EVENTS (TCP_CLOSE | TCP_ABORT | TCP_CONNECTED | TCP_TIMEDOUT) diff --git a/nuttx/net/icmp/icmp_ping.c b/nuttx/net/icmp/icmp_ping.c index 60806c7c5..e32ef2d31 100644 --- a/nuttx/net/icmp/icmp_ping.c +++ b/nuttx/net/icmp/icmp_ping.c @@ -349,7 +349,7 @@ int icmp_ping(in_addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, state.png_result = -ENOMEM; /* Assume allocation failure */ state.png_addr = addr; /* Address of the peer to be ping'ed */ state.png_id = id; /* The ID to use in the ECHO request */ - state.png_seqno = seqno; /* The seqno to use int the ECHO request */ + state.png_seqno = seqno; /* The seqno to use in the ECHO request */ state.png_datlen = datalen; /* The length of data to send in the ECHO request */ state.png_sent = false; /* ECHO request not yet sent */ diff --git a/nuttx/net/icmpv6/icmpv6_ping.c b/nuttx/net/icmpv6/icmpv6_ping.c index 888f8a8a9..5e67ac86d 100644 --- a/nuttx/net/icmpv6/icmpv6_ping.c +++ b/nuttx/net/icmpv6/icmpv6_ping.c @@ -39,11 +39,12 @@ #include #if defined(CONFIG_NET) && defined(CONFIG_NET_ICMPv6) && \ - defined(CONFIG_NET_ICMPv6v6_PING) + defined(CONFIG_NET_ICMPv6_PING) #include #include #include +#include #include #include @@ -55,24 +56,34 @@ #include #include #include +#include #include "netdev/netdev.h" #include "devif/devif.h" #include "arp/arp.h" +#include "utils/utils.h" #include "icmpv6/icmpv6.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +#define ETHBUF ((struct eth_hdr_s *)&dev->d_buf[0]) #define ICMPv6BUF ((struct icmpv6_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define ICMPv6DAT (&dev->d_buf[NET_LL_HDRLEN(dev) + sizeof(struct icmpv6_iphdr_s)]) +#define ICMPv6ECHOREQ \ + ((struct icmpv6_echo_request_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +#define ICMPv6ECHOREPLY \ + ((struct icmpv6_echo_reply_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) /* Allocate a new ICMPv6 data callback */ #define icmpv6_callback_alloc() devif_callback_alloc(&g_icmpv6_echocallback) #define icmpv6_callback_free(cb) devif_callback_free(cb, &g_icmpv6_echocallback) +/**************************************************************************** + * Private Data + ****************************************************************************/ + /**************************************************************************** * Private Types ****************************************************************************/ @@ -132,6 +143,96 @@ static inline int ping_timeout(FAR struct icmpv6_ping_s *pstate) return FALSE; } +/**************************************************************************** + * Name: icmpv6_echo_request + * + * Description: + * Format an ICMPv6 Echo Request message.. This version + * is for a standalone solicitation. If formats: + * + * - The Ethernet header + * - The IPv6 header + * - The ICMPv6 Echo Request Message + * + * Parameters: + * dev - Reference to an Ethernet device driver structure + * pstate - Ping state structure + * + * Return: + * None + * + ****************************************************************************/ + +static void icmpv6_echo_request(FAR struct net_driver_s *dev, + FAR struct icmpv6_ping_s *pstate) +{ + FAR struct icmpv6_iphdr_s *icmp; + FAR struct icmpv6_echo_request_s *req; + uint16_t reqlen; + int i; + + nllvdbg("Send ECHO request: seqno=%d\n", pstate->png_seqno); + + /* Set up the IPv6 header (most is probably already in place) */ + + icmp = ICMPv6BUF; + icmp->vtc = 0x60; /* Version/traffic class (MS) */ + icmp->tcf = 0; /* Traffic class (LS)/Flow label (MS) */ + icmp->flow = 0; /* Flow label (LS) */ + + /* Length excludes the IPv6 header */ + + reqlen = SIZEOF_ICMPV6_ECHO_REQUEST_S(pstate->png_datlen); + icmp->len[0] = (reqlen >> 8); + icmp->len[1] = (reqlen & 0xff); + + icmp->proto = IP_PROTO_ICMP6; /* Next header */ + icmp->ttl = 255; /* Hop limit */ + + /* Set the multicast destination IP address */ + + net_ipv6addr_copy(icmp->destipaddr, pstate->png_addr); + + /* Add out IPv6 address as the source address */ + + net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + + /* Set up the ICMPv6 Echo Request message */ + + req = ICMPv6ECHOREQ; + req->type = ICMPv6_ECHO_REQUEST; /* Message type */ + req->code = 0; /* Message qualifier */ + req->id = htons(pstate->png_id); + req->seqno = htons(pstate->png_seqno); + + /* Add some easily verifiable data */ + + for (i = 0; i < pstate->png_datlen; i++) + { + req->data[i] = i; + } + + /* Calculate the checksum over both the ICMP header and payload */ + + icmp->chksum = 0; + icmp->chksum = ~icmpv6_chksum(dev); + + /* Set the size to the size of the IPv6 header and the payload size */ + + IFF_SET_IPv6(dev->d_flags); + + dev->d_sndlen = reqlen; + dev->d_len = reqlen + IPv6_HDRLEN; + + nllvdbg("Outgoing ICMPv6 Echo Request length: %d (%d)\n", + dev->d_len, (icmp->len[0] << 8) | icmp->len[1]); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.icmpv6.sent++; + g_netstats.ip.sent++; +#endif +} + /**************************************************************************** * Function: ping_interrupt * @@ -158,8 +259,6 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, FAR void *pvpriv, uint16_t flags) { FAR struct icmpv6_ping_s *pstate = (struct icmpv6_ping_s *)pvpriv; - FAR uint8_t *ptr; - int i; nllvdbg("flags: %04x\n", flags); if (pstate) @@ -172,12 +271,12 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, if ((flags & ICMPv6_ECHOREPLY) != 0 && conn != NULL) { - FAR struct icmpv6_iphdr_s *icmpv6 = (FAR struct icmpv6_iphdr_s *)conn; + FAR struct icmpv6_echo_reply_s *reply = ICMPv6ECHOREPLY; nllvdbg("ECHO reply: id=%d seqno=%d\n", - ntohs(icmpv6->id), ntohs(icmpv6->seqno)); + ntohs(reply->id), reply(reply->seqno)); - if (ntohs(icmpv6->id) == pstate->png_id) + if (ntohs(reply->id) == pstate->png_id) { /* Consume the ECHOREPLY */ @@ -187,7 +286,7 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, /* Return the result to the caller */ pstate->png_result = OK; - pstate->png_seqno = ntohs(icmpv6->seqno); + pstate->png_seqno = ntohs(reply->seqno); goto end_wait; } } @@ -209,33 +308,9 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, (flags & ICMPv6_NEWDATA) == 0 && /* No incoming data */ !pstate->png_sent) /* Request not sent */ { - FAR struct icmpv6_iphdr_s *picmpv6 = ICMPv6BUF; - - /* We can send the ECHO request now. - * - * Format the ICMPv6 ECHO request packet - */ - - picmpv6->type = ICMPv6_ECHO_REQUEST; - picmpv6->icode = 0; -# error "IPv6 ECHO Request not implemented" - - /* Add some easily verifiable data */ - - for (i = 0, ptr = ICMPv6DAT; i < pstate->png_datlen; i++) - { - *ptr++ = i; - } - - /* Send the ICMPv6 echo request. Note that d_sndlen is set to - * the size of the ICMPv6 payload and does not include the size - * of the ICMPv6 header. - */ + /* Send the ECHO request now. */ - nllvdbg("Send ECHO request: seqno=%d\n", pstate->png_seqno); - - dev->d_sndlen = pstate->png_datlen + 4; - icmpv6_send(dev, &pstate->png_addr); + icmpv6_echo_request(dev, pstate); pstate->png_sent = true; return flags; } @@ -250,7 +325,8 @@ static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, * device. */ - if (!net_ipv6addr_maskcmp(pstate->png_addr, dev->d_ipaddr, dev->d_netmask)) + if (!net_ipv6addr_maskcmp(pstate->png_addr, dev->d_ipv6addr, + dev->d_ipv6netmask)) { /* Destination address was not on the local network served by this * device. If a timeout occurs, then the most likely reason is @@ -328,12 +404,13 @@ int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, { struct icmpv6_ping_s state; net_lock_t save; -#ifdef CONFIG_NET_ARP_SEND + +#ifdef CONFIG_NET_ICMPv6_SEND int ret; - /* Make sure that the IP address mapping is in the ARP table */ + /* Make sure that the IP address mapping is in the Neighbor Table */ - ret = arp_send(addr); + ret = neighbor_send(addr); if (ret < 0) { ndbg("ERROR: Not reachable\n"); @@ -344,13 +421,14 @@ int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, /* Initialize the state structure */ sem_init(&state.png_sem, 0, 0); - state.png_ticks = DSEC2TICK(dsecs); /* System ticks to wait */ - state.png_result = -ENOMEM; /* Assume allocation failure */ - state.png_addr = addr; /* Address of the peer to be ping'ed */ - state.png_id = id; /* The ID to use in the ECHO request */ - state.png_seqno = seqno; /* The seqno to use int the ECHO request */ - state.png_datlen = datalen; /* The length of data to send in the ECHO request */ - state.png_sent = false; /* ECHO request not yet sent */ + state.png_ticks = DSEC2TICK(dsecs); /* System ticks to wait */ + state.png_result = -ENOMEM; /* Assume allocation failure */ + state.png_id = id; /* The ID to use in the ECHO request */ + state.png_seqno = seqno; /* The seqno to use in the ECHO request */ + state.png_datlen = datalen; /* The length of data to send in the ECHO request */ + state.png_sent = false; /* ECHO request not yet sent */ + + net_ipv6addr_copy(state.png_addr, addr); /* Address of the peer to be ping'ed */ save = net_lock(); state.png_time = clock_systimer(); @@ -404,4 +482,4 @@ int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, } } -#endif /* CONFIG_NET_ICMPv6 && CONFIG_NET_ICMPv6v6_PING ... */ +#endif /* CONFIG_NET_ICMPv6 && CONFIG_NET_ICMPv6_PING ... */ diff --git a/nuttx/net/icmpv6/icmpv6_solicit.c b/nuttx/net/icmpv6/icmpv6_solicit.c index a03f6ba89..6e23119e0 100644 --- a/nuttx/net/icmpv6/icmpv6_solicit.c +++ b/nuttx/net/icmpv6/icmpv6_solicit.c @@ -80,7 +80,12 @@ static const uint16_t g_icmpv_mcastaddr[6] = * Name: icmpv6_solicit * * Description: - * Set up to send an ICMPv6 Neighbor Solicitation message + * Set up to send an ICMPv6 Neighbor Solicitation message. This version + * is for a standalone solicitation. If formats: + * + * - The Ethernet header + * - The IPv6 header + * - The ICMPv6 Neighbor Solicitation Message * * Parameters: * dev - Reference to an Ethernet device driver structure -- cgit v1.2.3