feat(icmp): Support IPv6 ping and add icmp example

This commit is contained in:
yuanjm
2020-11-02 14:04:09 +08:00
parent dda5062c80
commit 370ac3e48a
12 changed files with 553 additions and 37 deletions

View File

@ -18,10 +18,11 @@
#include "lwip/ip_addr.h"
typedef struct _ping_option {
ip4_addr_t ping_target;
ip_addr_t ping_target;
uint32_t ping_count;
uint32_t ping_rcv_timeout;
uint32_t ping_delay;
uint32_t interface;
size_t ping_data_len;
uint16_t ping_id;
u8_t ping_tos;
@ -42,13 +43,16 @@ esp_err_t esp_ping_set_target(ping_target_id_t opt_id, void *opt_val, uint32_t o
switch (opt_id) {
case PING_TARGET_IP_ADDRESS:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
ping_option_info->ping_target.addr = *(uint32_t *)opt_val;
ipaddr_aton(opt_val, &(ping_option_info->ping_target));
break;
case PING_TARGET_IP_ADDRESS_COUNT:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
ping_option_info->ping_count = *(uint32_t *)opt_val;
break;
case PING_TARGET_IF_INDEX:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
ping_option_info->interface = *(uint32_t *)opt_val;
break;
case PING_TARGET_RCV_TIMEO:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
ping_option_info->ping_rcv_timeout = (*(uint32_t *)opt_val);
@ -93,13 +97,16 @@ esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t o
switch (opt_id) {
case PING_TARGET_IP_ADDRESS:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
*(uint32_t *)opt_val = ping_option_info->ping_target.addr;
ip_addr_copy(*(ip_addr_t*)opt_val, ping_option_info->ping_target);
break;
case PING_TARGET_IP_ADDRESS_COUNT:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
*(uint32_t *)opt_val = ping_option_info->ping_count;
break;
case PING_TARGET_IF_INDEX:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
*(uint32_t *)opt_val = ping_option_info->interface;
break;
case PING_TARGET_RCV_TIMEO:
ESP_PING_CHECK_OPTLEN(opt_len, uint32_t);
*(uint32_t *)opt_val = ping_option_info->ping_rcv_timeout;

View File

@ -268,22 +268,23 @@ ping_init(void)
uint32_t ping_timeout = PING_RCV_TIMEO;
uint32_t ping_delay = PING_DELAY;
uint32_t ping_count_max = 3;
ip_addr_t ping_target;
ip4_addr_t ipaddr;
uint32_t interface = 0;
ip_addr_t ipaddr;
esp_ping_get_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count_max, sizeof(ping_count_max));
esp_ping_get_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(ping_timeout));
esp_ping_get_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(ping_delay));
esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr.addr, sizeof(uint32_t));
esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr, sizeof(ip_addr_t));
esp_ping_get_target(PING_TARGET_IP_TOS, &tos, sizeof(tos));
ip_addr_copy_from_ip4(ping_target, ipaddr);
esp_ping_get_target(PING_TARGET_IF_INDEX, &interface, sizeof(interface));
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
config.count = ping_count_max;
config.timeout_ms = ping_timeout;
config.interval_ms = ping_delay;
config.target_addr = ping_target;
config.target_addr = ipaddr;
config.tos = tos;
config.interface = interface;
esp_ping_callbacks_t cbs = {
.on_ping_end = on_ping_end,

View File

@ -80,7 +80,9 @@ static esp_err_t esp_ping_send(esp_ping_t *ep)
ep->packet_hdr->seqno++;
/* generate checksum since "seqno" has changed */
ep->packet_hdr->chksum = 0;
ep->packet_hdr->chksum = inet_chksum(ep->packet_hdr, ep->icmp_pkt_size);
if (ep->packet_hdr->type == ICMP_ECHO) {
ep->packet_hdr->chksum = inet_chksum(ep->packet_hdr, ep->icmp_pkt_size);
}
int sent = sendto(ep->sock, ep->packet_hdr, ep->icmp_pkt_size, 0,
(struct sockaddr *)&ep->target_addr, sizeof(ep->target_addr));
@ -103,26 +105,27 @@ static int esp_ping_receive(esp_ping_t *ep)
int len = 0;
struct sockaddr_storage from;
int fromlen = sizeof(from);
uint16_t data_head = (uint16_t)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr));
uint16_t data_head = 0;
while ((len = recvfrom(ep->sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) > 0) {
if (len >= data_head) {
if (from.ss_family == AF_INET) {
// IPv4
struct sockaddr_in *from4 = (struct sockaddr_in *)&from;
inet_addr_to_ip4addr(ip_2_ip4(&ep->recv_addr), &from4->sin_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V4);
}
if (from.ss_family == AF_INET) {
// IPv4
struct sockaddr_in *from4 = (struct sockaddr_in *)&from;
inet_addr_to_ip4addr(ip_2_ip4(&ep->recv_addr), &from4->sin_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V4);
data_head = (uint16_t)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr));
}
#if LWIP_IPV6
else {
// IPv6
struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from;
inet6_addr_to_ip6addr(ip_2_ip6(&ep->recv_addr), &from6->sin6_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V6);
}
else {
// IPv6
struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from;
inet6_addr_to_ip6addr(ip_2_ip6(&ep->recv_addr), &from6->sin6_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V6);
data_head = (uint16_t)(sizeof(struct ip6_hdr) + sizeof(struct icmp6_echo_hdr));
}
#endif
// Currently we only process IPv4
if (IP_IS_V4_VAL(ep->recv_addr)) {
if (len >= data_head) {
if (IP_IS_V4_VAL(ep->recv_addr)) { // Currently we process IPv4
struct ip_hdr *iphdr = (struct ip_hdr *)buf;
struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
if ((iecho->id == ep->packet_hdr->id) && (iecho->seqno == ep->packet_hdr->seqno)) {
@ -132,6 +135,17 @@ static int esp_ping_receive(esp_ping_t *ep)
return len;
}
}
#if LWIP_IPV6
else if (IP_IS_V6_VAL(ep->recv_addr)) { // Currently we process IPv6
struct ip6_hdr *iphdr = (struct ip6_hdr *)buf;
struct icmp6_echo_hdr *iecho6 = (struct icmp6_echo_hdr *)(buf + sizeof(struct ip6_hdr)); // IPv6 head length is 40
if ((iecho6->id == ep->packet_hdr->id) && (iecho6->seqno == ep->packet_hdr->seqno)) {
ep->received++;
ep->recv_len = IP6H_PLEN(iphdr) - sizeof(struct icmp6_echo_hdr); //The data portion of ICMPv6
return len;
}
}
#endif
}
fromlen = sizeof(from);
}
@ -229,7 +243,6 @@ esp_err_t esp_ping_new_session(const esp_ping_config_t *config, const esp_ping_c
ep->packet_hdr = mem_calloc(1, ep->icmp_pkt_size);
PING_CHECK(ep->packet_hdr, "no memory for echo packet", err, ESP_ERR_NO_MEM);
/* set ICMP type and code field */
ep->packet_hdr->type = ICMP_ECHO;
ep->packet_hdr->code = 0;
/* ping id should be unique, treat task handle as ping ID */
ep->packet_hdr->id = ((uint32_t)ep->ping_task_hdl) & 0xFFFF;
@ -238,21 +251,29 @@ esp_err_t esp_ping_new_session(const esp_ping_config_t *config, const esp_ping_c
for (uint32_t i = 0; i < config->data_size; i++) {
d[i] = 'A' + i;
}
#if LWIP_IPV4 && LWIP_IPV6
if (IP_IS_V4(&config->target_addr) || ip6_addr_isipv4mappedipv6(ip_2_ip6(&config->target_addr))) {
ep->sock = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
} else {
ep->sock = socket(AF_INET6, SOCK_RAW, IP6_NEXTH_ICMP6);
}
/* create socket */
if (IP_IS_V4(&config->target_addr) || ip6_addr_isipv4mappedipv6(ip_2_ip6(&config->target_addr))) {
ep->sock = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
} else {
ep->sock = socket(AF_INET6, SOCK_RAW, IP6_NEXTH_ICMP6);
}
#else
if (IP_IS_V4(&config->target_addr)) {
ep->sock = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
}
#endif
PING_CHECK(ep->sock > 0, "create socket failed: %d", err, ESP_FAIL, ep->sock);
/* set if index */
if(config->interface) {
struct ifreq iface;
if(netif_index_to_name(config->interface, iface.ifr_name) == NULL) {
goto err;
}
if(setsockopt(ep->sock, SOL_SOCKET, SO_BINDTODEVICE, &iface, sizeof(iface)) !=0) {
goto err;
}
}
struct timeval timeout;
timeout.tv_sec = config->timeout_ms / 1000;
timeout.tv_usec = (config->timeout_ms % 1000) * 1000;
@ -267,12 +288,14 @@ esp_err_t esp_ping_new_session(const esp_ping_config_t *config, const esp_ping_c
struct sockaddr_in *to4 = (struct sockaddr_in *)&ep->target_addr;
to4->sin_family = AF_INET;
inet_addr_from_ip4addr(&to4->sin_addr, ip_2_ip4(&config->target_addr));
ep->packet_hdr->type = ICMP_ECHO;
}
#if LWIP_IPV6
if (IP_IS_V6(&config->target_addr)) {
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ep->target_addr;
to6->sin6_family = AF_INET6;
inet6_addr_from_ip6addr(&to6->sin6_addr, ip_2_ip6(&config->target_addr));
ep->packet_hdr->type = ICMP6_TYPE_EREQ;
}
#endif
/* return ping handle to user */

View File

@ -54,7 +54,8 @@ typedef enum {
PING_TARGET_RES_FN = 55, /**< ping result callback function */
PING_TARGET_RES_RESET = 56, /**< ping result statistic reset */
PING_TARGET_DATA_LEN = 57, /**< ping data length*/
PING_TARGET_IP_TOS = 58 /**< ping QOS*/
PING_TARGET_IP_TOS = 58, /**< ping QOS*/
PING_TARGET_IF_INDEX = 59 /**< ping if index*/
} ping_target_id_t;
typedef enum {

View File

@ -71,6 +71,7 @@ typedef struct {
ip_addr_t target_addr; /*!< Target IP address, either IPv4 or IPv6 */
uint32_t task_stack_size; /*!< Stack size of internal ping task */
uint32_t task_prio; /*!< Priority of internal ping task */
uint32_t interface; /*!< Netif index, interface=0 means NETIF_NO_INDEX*/
} esp_ping_config_t;
/**
@ -92,6 +93,7 @@ typedef struct {
.target_addr = PING_TARGET_ADDR, \
.task_stack_size = 2048, \
.task_prio = 2, \
.interface = 0,\
}
#define ESP_PING_COUNT_INFINITE (0) /*!< Set ping count to zero will ping target infinitely */

View File

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(icmp-echo)

View File

@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := icmp-echo
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,105 @@
# ICMP Echo-Reply (Ping) example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Ping is a useful network utility used to test if a remote host is reachable on the IP network. It measures the round-trip time for messages sent from the source host to a destination target that are echoed back to the source.
Ping operates by sending Internet Control Message Protocol (ICMP) echo request packets to the target host and waiting for an ICMP echo reply.
**Notes:** Currently this example supports IPv4 & IPv6. When ping IPv6 address should set interface (-I) to 2.
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP8266 development board.
### Configure the project
```
idf.py menuconfig
```
In the `Example Connection Configuration` menu:
* If Wi-Fi interface is selected, you also have to set:
* Wi-Fi SSID and Wi-Fi password that your board will connect to.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP8266_RTOS_SDK to build projects.
## Example Output
```bash
==========================================================================
| PING help command |
| send ICMP ECHO_REQUEST to network hosts |
| |
| ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] <host> |
| -W, --timeout=<t> Time to wait for a response, in seconds |
| -i, --interval=<t> Wait interval seconds between sending each packet |
| -s, --size=<n> Specify the number of data bytes to be sent |
| -c, --count=<n> Stop after sending count packets |
| -Q, --tos=<n> Set Type of Service related bits in IP datagrams |
| -I, --interface=<n> Set Interface number to send out the packet |
| <host> Host address or IP address |
| |
===========================================================================
```
* Run `ping` command to test reachable of remote server.
```bash
esp8266> ping www.espressif.com
64 bytes from 119.9.92.99 icmp_seq=1 ttl=51 time=36 ms
64 bytes from 119.9.92.99 icmp_seq=2 ttl=51 time=34 ms
64 bytes from 119.9.92.99 icmp_seq=3 ttl=51 time=37 ms
64 bytes from 119.9.92.99 icmp_seq=4 ttl=51 time=36 ms
64 bytes from 119.9.92.99 icmp_seq=5 ttl=51 time=33 ms
--- 119.9.92.99 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 176ms
```
* Run `ping` with a wrong domain name
```bash
esp8266> ping www.hello-world.io
ping: unknown host www.hello-world.io
Command returned non-zero error code: 0x1 (ERROR)
```
* Run `ping` with an unreachable server
```bash
esp8266> ping www.zoom.us
From 69.171.230.18 icmp_seq=1 timeout
From 69.171.230.18 icmp_seq=2 timeout
From 69.171.230.18 icmp_seq=3 timeout
From 69.171.230.18 icmp_seq=4 timeout
From 69.171.230.18 icmp_seq=5 timeout
--- 69.171.230.18 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4996ms
```
* Run `ping` with a IPv6 address
```bash
esp8266> ping fe80::a33a:d586:a603:5bb2 -I 2
64 bytes from FE80::A33A:D586:A603:5BB2 icmp_seq=1 ttl=0 time=7 ms
esp8266> 64 bytes from FE80::A33A:D586:A603:5BB2 icmp_seq=2 ttl=0 time=19 ms
64 bytes from FE80::A33A:D586:A603:5BB2 icmp_seq=3 ttl=0 time=3 ms
64 bytes from FE80::A33A:D586:A603:5BB2 icmp_seq=4 ttl=0 time=7 ms
64 bytes from FE80::A33A:D586:A603:5BB2 icmp_seq=5 ttl=0 time=4 ms
--- FE80::A33A:D586:A603:5BB2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 40ms
```

View File

@ -0,0 +1,27 @@
from __future__ import unicode_literals
import re
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
def test_examples_icmp_echo(env, extra_data):
dut = env.get_dut('icmp_echo', 'examples/protocols/icmp_echo')
dut.start_app()
dut.expect('example_connect: Connected to')
dut.expect('esp>')
dut.write('ping www.espressif.com')
ip_re = r'\.'.join((r'\d{1,3}',) * 4)
ip = dut.expect(re.compile(r'64 bytes from ({}) icmp_seq=1 ttl=\d+ time=\d+ ms'.format(ip_re)))[0]
# expect at least one more (there could be lost packets)
dut.expect(re.compile(r'64 bytes from {} icmp_seq=[2-5] ttl=\d+ time='.format(ip)))
dut.expect(re.compile(r'5 packets transmitted, [2-5] received, \d{1,3}% packet loss'))
dut.write('')
dut.expect('esp>')
if __name__ == '__main__':
test_examples_icmp_echo()

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "echo_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,323 @@
/* ICMP echo example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_console.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "ping/ping_sock.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, ipaddr_ntoa((ip_addr_t*)&target_addr), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n",ipaddr_ntoa((ip_addr_t*)&target_addr), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
}
#if LWIP_IPV6
else {
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
#endif
printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
esp_ping_delete_session(hdl);
}
static struct {
struct arg_dbl *timeout;
struct arg_dbl *interval;
struct arg_int *data_size;
struct arg_int *count;
struct arg_int *tos;
struct arg_int *interface;
struct arg_str *host;
struct arg_end *end;
} ping_args;
static int do_ping_cmd(int argc, char **argv)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
if (ping_args.timeout->count > 0) {
config.timeout_ms = (uint32_t)(ping_args.timeout->dval[0] * 1000);
}
if (ping_args.interval->count > 0) {
config.interval_ms = (uint32_t)(ping_args.interval->dval[0] * 1000);
}
if (ping_args.data_size->count > 0) {
config.data_size = (uint32_t)(ping_args.data_size->ival[0]);
}
if (ping_args.count->count > 0) {
config.count = (uint32_t)(ping_args.count->ival[0]);
}
if (ping_args.tos->count > 0) {
config.tos = (uint32_t)(ping_args.tos->ival[0]);
}
if (ping_args.interface->count > 0) {
config.interface = (uint32_t)(ping_args.interface->ival[0]);
}
// parse IP address
ip_addr_t target_addr;
memset(&target_addr, 0, sizeof(target_addr));
#if LWIP_IPV6
struct sockaddr_in6 sock_addr6;
if (inet_pton(AF_INET6, ping_args.host->sval[0], &sock_addr6.sin6_addr) == 1) {
/* convert ip6 string to ip6 address */
ipaddr_aton(ping_args.host->sval[0], &target_addr);
} else
#endif
{
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
/* convert ip4 string or hostname to ip4 or ip6 address */
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
}
#if LWIP_IPV6
else {
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
}
#endif
freeaddrinfo(res);
}
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
static void register_ping(void)
{
ping_args.timeout = arg_dbl0("W", "timeout", "<t>", "Time to wait for a response, in seconds");
ping_args.interval = arg_dbl0("i", "interval", "<t>", "Wait interval seconds between sending each packet");
ping_args.data_size = arg_int0("s", "size", "<n>", "Specify the number of data bytes to be sent");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
ping_args.interface = arg_int0("I", "interface", "<n>", "Set Interface number to send out the packet");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {
.command = "ping",
.help = "send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &do_ping_cmd,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&ping_cmd));
}
/* handle 'quit' command */
static int do_cmd_quit(int argc, char **argv)
{
printf("ByeBye\r\n");
return 0;
}
static esp_err_t register_quit(void)
{
esp_console_cmd_t command = {
.command = "quit",
.help = "Quit REPL environment",
.func = &do_cmd_quit
};
return esp_console_cmd_register(&command);
}
static void initialize_console(void)
{
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = 32,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK( esp_console_init(&console_config) );
/* Configure linenoise line completion library */
/* Enable multiline editing. If not set, long commands will scroll within
* single line.
*/
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback *) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* wait for active network connection */
ESP_ERROR_CHECK(example_connect());
initialize_console();
/* register command `ping` */
register_ping();
/* register command `quit` */
register_quit();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char *prompt = LOG_COLOR_I "esp8266> " LOG_RESET_COLOR;
printf("\n ==========================================================================\n");
printf(" | PING help command |\n");
printf(" | send ICMP ECHO_REQUEST to network hosts |\n");
printf(" | |\n");
printf(" | ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] <host> |\n");
printf(" | -W, --timeout=<t> Time to wait for a response, in seconds |\n");
printf(" | -i, --interval=<t> Wait interval seconds between sending each packet |\n");
printf(" | -s, --size=<n> Specify the number of data bytes to be sent |\n");
printf(" | -c, --count=<n> Stop after sending count packets |\n");
printf(" | -Q, --tos=<n> Set Type of Service related bits in IP datagrams |\n");
printf(" | -I, --interface=<n> Set Interface number to send out the packet |\n");
printf(" | <host> Host address or IP address |\n");
printf(" | |\n");
printf(" ===========================================================================\n\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "esp8266> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
while (true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char *line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x\n", ret);
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
}