mirror of
https://github.com/pine64/bl_iot_sdk.git
synced 2024-10-06 06:50:19 +00:00
419 lines
14 KiB
C
419 lines
14 KiB
C
/*
|
|
* Copyright (c) 2020 Bouffalolab.
|
|
*
|
|
* This file is part of
|
|
* *** Bouffalolab Software Dev Kit ***
|
|
* (see www.bouffalolab.com).
|
|
*
|
|
* 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 of Bouffalo Lab 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 HOLDER 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.
|
|
*/
|
|
/*
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*
|
|
* This file is part of the lwIP TCP/IP stack.
|
|
*
|
|
*/
|
|
|
|
#include <lwip/opt.h>
|
|
#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
|
|
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <lwip/mem.h>
|
|
#include <lwip/raw.h>
|
|
#include <lwip/icmp.h>
|
|
#include <lwip/netif.h>
|
|
#include <lwip/sys.h>
|
|
#include <lwip/timeouts.h>
|
|
#include <lwip/inet_chksum.h>
|
|
#include <lwip/prot/ip4.h>
|
|
#include <lwip/dns.h>
|
|
#include <lwip/tcpip.h>
|
|
#include <lwip/netdb.h>
|
|
#include <lwip/inet.h>
|
|
#include <utils_list.h>
|
|
#include <utils_log.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <utils_getopt.h>
|
|
#include <bl_sec.h>
|
|
#include <cli.h>
|
|
#include <ping.h>
|
|
#include <utils_memp.h>
|
|
|
|
#define LOG_DISP 0
|
|
|
|
#define PING_TOTAL_NODE_LIMIT 10
|
|
#define DEFAULT_PING_INTERVAL 1000
|
|
#define DEFAULT_PING_SIZE 32
|
|
#define DEFAULT_PING_TIMEOUT 5000
|
|
|
|
static struct t_hdr* find_and_extract(struct utils_list *list, u32_t seq);
|
|
static void ping_free(void *arg);
|
|
static void ping_usage();
|
|
|
|
static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, void *arg)
|
|
{
|
|
size_t i;
|
|
size_t data_len = len - sizeof(struct icmp_echo_hdr);
|
|
struct ping_var *env = (struct ping_var *)arg;
|
|
ICMPH_TYPE_SET(iecho, ICMP_ECHO);
|
|
ICMPH_CODE_SET(iecho, 0);
|
|
iecho->chksum = 0;
|
|
iecho->id = env->id;
|
|
iecho->seqno = lwip_htons(++env->seq_num);
|
|
|
|
for(i = 0; i < data_len; i++) {
|
|
((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
|
|
}
|
|
iecho->chksum = inet_chksum(iecho, len);
|
|
}
|
|
|
|
/* Ping using the raw ip */
|
|
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr)
|
|
{
|
|
struct icmp_echo_hdr *iecho;
|
|
struct t_hdr *find_hdr;
|
|
struct ping_var *env = (struct ping_var*)arg;
|
|
struct icmp_echo_hdr echo_hdr;
|
|
LWIP_UNUSED_ARG(arg);
|
|
LWIP_UNUSED_ARG(pcb);
|
|
LWIP_UNUSED_ARG(addr);
|
|
LWIP_ASSERT("p != NULL", p != NULL);
|
|
#if LOG_DISP
|
|
log_info("recv arg %p\r\n", arg);
|
|
log_info("recv now\r\n");
|
|
log_info("recv addr %s\r\n", ipaddr_ntoa(addr));
|
|
#endif
|
|
|
|
if ((p->tot_len == (PBUF_IP_HLEN + sizeof(struct icmp_echo_hdr)) + env->data_size) && ip_addr_cmp(&env->dest, addr)) {
|
|
memcpy(&echo_hdr, p->payload + PBUF_IP_HLEN, sizeof(struct icmp_echo_hdr));
|
|
iecho = &echo_hdr;
|
|
|
|
if ((iecho->type == 0) && (iecho->code == 0) && (iecho->id == env->id) && env->node_num > 0) {
|
|
find_hdr = find_and_extract(&env->req_list, iecho->seqno);
|
|
|
|
if (find_hdr) {
|
|
printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%lu ms\r\n ", p->tot_len, ipaddr_ntoa(&env->dest), ntohs(iecho->seqno), *((uint8_t *)p->payload + 8), (sys_now() - find_hdr->send_time));
|
|
|
|
utils_memp_free(env->pool, find_hdr);
|
|
env->node_num--;
|
|
pbuf_free(p);
|
|
return 1; /* eat the packet */
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; /* don't eat the packet */
|
|
}
|
|
|
|
static void ping_send(struct ping_var *env)
|
|
{
|
|
struct pbuf *p = NULL;
|
|
struct icmp_echo_hdr *iecho;
|
|
struct t_hdr *time_hdr = NULL;
|
|
size_t ping_size = sizeof(struct icmp_echo_hdr) + env->data_size;
|
|
LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff);
|
|
p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
|
|
if (!p) {
|
|
goto clean;
|
|
}
|
|
|
|
if ((p->len == p->tot_len) && (p->next == NULL)) {
|
|
iecho = (struct icmp_echo_hdr *)p->payload;
|
|
time_hdr = (struct t_hdr*)utils_memp_malloc(env->pool);
|
|
if (!time_hdr) {
|
|
goto clean;
|
|
}
|
|
#if LOG_DISP
|
|
log_info("env-adress %p\r\n", env);
|
|
log_info("env--dest %s\r\n", ipaddr_ntoa(&env->dest));
|
|
#endif
|
|
ping_prepare_echo(iecho, (u16_t)ping_size, env);
|
|
time_hdr->ping_seq = iecho->seqno;
|
|
|
|
raw_sendto(env->pcb, p, &env->dest);
|
|
time_hdr->send_time = sys_now();
|
|
utils_list_push_back(&env->req_list, (struct utils_list_hdr*)time_hdr);
|
|
env->node_num++;
|
|
env->requests_count++;
|
|
}
|
|
|
|
clean:
|
|
if (p) {
|
|
pbuf_free(p);
|
|
}
|
|
}
|
|
|
|
static void ping_timeout(void *arg)
|
|
{
|
|
struct ping_var *env = (struct ping_var*)arg;
|
|
|
|
#if LOG_DISP
|
|
log_info("%d\r\n", env->interval);
|
|
log_info("env-adress%p\r\n", env);
|
|
log_info("env--dest%s\r\n", ipaddr_ntoa(&env->dest));
|
|
#endif
|
|
while (env->req_list.first && sys_now() - ((struct t_hdr*)(env->req_list.first))->send_time > env->timeout){
|
|
printf("The sequence number %d timed out\r\n", ntohs(((struct t_hdr*)(env->req_list.first))->ping_seq));
|
|
utils_memp_free(env->pool, (struct t_hdr*)(utils_list_pop_front(&env->req_list)));
|
|
env->node_num--;
|
|
}
|
|
if (env->node_num == PING_TOTAL_NODE_LIMIT) {
|
|
utils_memp_free(env->pool, (struct t_hdr*)(utils_list_pop_front(&env->req_list)));
|
|
env->node_num--;
|
|
#if LOG_DISP
|
|
log_info("send node_num %d\r\n", env->node_num);
|
|
#endif
|
|
}
|
|
ping_send(env);
|
|
|
|
#if LOG_DISP
|
|
log_info("%s\r\n", ipaddr_ntoa(&env->dest));
|
|
log_info("request_count%d\r\n", env->requests_count);
|
|
#endif
|
|
sys_timeout(env->interval, ping_timeout, env);
|
|
|
|
#if LOG_DISP
|
|
log_info("%d\r\n", env->interval);
|
|
log_info("totol_count%d\r\n", env->total_count);
|
|
#endif
|
|
|
|
if (env->requests_count == env->total_count) {
|
|
sys_untimeout(ping_timeout, env);
|
|
sys_timeout(env->timeout, ping_free, env);
|
|
}
|
|
|
|
}
|
|
|
|
static void ping_free(void *arg)
|
|
{
|
|
struct ping_var *env = (struct ping_var*)arg;
|
|
while (env->req_list.first != NULL) {
|
|
printf("The sequence number %d timed out\r\n", ntohs(((struct t_hdr*)(env->req_list.first))->ping_seq));
|
|
utils_memp_free(env->pool, utils_list_pop_front(&env->req_list));
|
|
env->node_num--;
|
|
}
|
|
raw_remove(env->pcb);
|
|
utils_memp_deinit(env->pool);
|
|
vPortFree(env);
|
|
}
|
|
|
|
static int ping_init(void *arg)
|
|
{
|
|
struct ping_var *env = (struct ping_var*)arg;
|
|
struct raw_pcb *pcb;
|
|
if (utils_memp_init(&env->pool, sizeof(struct t_hdr), PING_TOTAL_NODE_LIMIT, sizeof(void *))) {
|
|
printf("pool_alloc failed\n");
|
|
return -1;
|
|
}
|
|
utils_list_init(&env->req_list);
|
|
#if LOG_DISP
|
|
log_info("env addr is %p\r\n", env);
|
|
log_info("env-dest%s \r\n", ipaddr_ntoa(&env->dest));
|
|
#endif
|
|
|
|
LOCK_TCPIP_CORE();
|
|
pcb = raw_new(IP_PROTO_ICMP);
|
|
|
|
if (!pcb) {
|
|
UNLOCK_TCPIP_CORE();
|
|
printf("pcb null, maybe the pcb pool or sys_timeout pool is empty\r\n");
|
|
return -1;
|
|
}
|
|
env->pcb = pcb;
|
|
raw_recv(pcb, ping_recv, env);
|
|
raw_bind(pcb, IP_ADDR_ANY);
|
|
sys_timeout(0, ping_timeout, env);
|
|
UNLOCK_TCPIP_CORE();
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct ping_var *ping_api_init(u16_t interval, u16_t size, u32_t count, u16_t timeout, ip_addr_t *dest)
|
|
{
|
|
struct ping_var *env = NULL;
|
|
env = (struct ping_var*)pvPortMalloc(sizeof(struct ping_var));
|
|
if (!env){
|
|
printf("mem malloc failed!\r\n");
|
|
return NULL;
|
|
}
|
|
env->total_count = count;
|
|
env->requests_count = 0;
|
|
env->interval = interval;
|
|
env->data_size = size;
|
|
env->seq_num = 0;
|
|
env->id = bl_rand();
|
|
env->timeout = timeout;
|
|
env->node_num = 0;
|
|
env->dest = *dest;
|
|
if (ping_init(env) != 0) {
|
|
printf("ping_init failed\r\n");
|
|
vPortFree(env);
|
|
}
|
|
return env;
|
|
}
|
|
|
|
static void ping_cmd(char *buf, int len, int argc, char **argv)
|
|
{
|
|
//
|
|
// -i interval
|
|
// -s size
|
|
// -c count
|
|
// -W timeout
|
|
int opt;
|
|
getopt_env_t getopt_env;
|
|
utils_getopt_init(&getopt_env, 0);
|
|
u16_t interval = DEFAULT_PING_INTERVAL;
|
|
u16_t data_size = DEFAULT_PING_SIZE;
|
|
u32_t total_count = 0;
|
|
u16_t timeout = DEFAULT_PING_TIMEOUT;
|
|
ip_addr_t *dest = NULL;
|
|
|
|
while ((opt = utils_getopt(&getopt_env, argc, argv, ":i:s:c:W:h")) != -1) {
|
|
switch (opt) {
|
|
case 'i':
|
|
interval = atoi(getopt_env.optarg);
|
|
break;
|
|
case 's':
|
|
data_size = atoi(getopt_env.optarg);
|
|
break;
|
|
case 'c':
|
|
total_count = atoi(getopt_env.optarg);
|
|
#if LOG_DISP
|
|
log_info("totol_count%d\r\n", total_count);
|
|
#endif
|
|
break;
|
|
case 'W':
|
|
timeout = atoi(getopt_env.optarg);
|
|
break;
|
|
case 'h':
|
|
ping_usage();
|
|
return;
|
|
case ':':
|
|
printf("%s: %c requires an argument\r\n", *argv, getopt_env.optopt);
|
|
ping_usage();
|
|
return;
|
|
case '?':
|
|
printf("%s: unknown option %c\r\n", *argv, getopt_env.optopt);
|
|
ping_usage();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (getopt_env.optind + 1 == argc) {
|
|
struct hostent *hostinfo = gethostbyname(argv[getopt_env.optind]);
|
|
if (!hostinfo) {
|
|
printf("Failed to resolve domain name\r\n");
|
|
}
|
|
else
|
|
{
|
|
dest = (ip_addr_t *)hostinfo->h_addr;
|
|
ping_api_init(interval, data_size, total_count, timeout, dest);
|
|
}
|
|
} else {
|
|
printf("Need target address\r\n");
|
|
ping_usage();
|
|
}
|
|
return;
|
|
}
|
|
|
|
static struct t_hdr* find_and_extract(struct utils_list *list, u32_t seq)
|
|
{
|
|
struct t_hdr *scan_list;
|
|
struct t_hdr *find= NULL;
|
|
scan_list = (struct t_hdr*)list->first;
|
|
if (scan_list == NULL || scan_list->ping_seq > seq)
|
|
return NULL;
|
|
if (scan_list->ping_seq == seq){
|
|
find = scan_list;
|
|
list->first = (struct utils_list_hdr*)scan_list->next;
|
|
}
|
|
else
|
|
{
|
|
while ((scan_list->next != NULL) && (scan_list->next->ping_seq < seq))
|
|
{
|
|
scan_list = scan_list->next;
|
|
}
|
|
if (scan_list->next != NULL && scan_list->next->ping_seq == seq)
|
|
{
|
|
if (list->last == (struct utils_list_hdr*)scan_list->next)
|
|
{
|
|
list->last = (struct utils_list_hdr*)scan_list;
|
|
}
|
|
find = scan_list->next;
|
|
scan_list->next = find->next;
|
|
}
|
|
}
|
|
return find;
|
|
}
|
|
|
|
#define PING_USAGE \
|
|
"ping [-c count] [-i interval] [-W timeout] [-s size] [-h help] destination\r\n" \
|
|
"\t\t-c count of ping requests. default is infinite\r\n" \
|
|
"\t\t-i interval in ms. default is 1000\r\n" \
|
|
"\t\t-W timeout in ms. default is 5000\r\n" \
|
|
"\t\t-s ICMP payload size in bytes. default is 32\r\n" \
|
|
"\t\t-h print this help\r\n"
|
|
|
|
static void ping_usage()
|
|
{
|
|
printf("%s", PING_USAGE);
|
|
}
|
|
|
|
// STATIC_CLI_CMD_ATTRIBUTE makes this(these) command(s) static
|
|
const static struct cli_command cmds_user[] STATIC_CLI_CMD_ATTRIBUTE = {
|
|
{ "ping", PING_USAGE, ping_cmd},
|
|
};
|
|
|
|
int network_netutils_ping_cli_register()
|
|
{
|
|
// static command(s) do NOT need to call aos_cli_register_command(s) to register.
|
|
// However, calling aos_cli_register_command(s) here is OK but is of no effect as cmds_user are included in cmds list.
|
|
// XXX NOTE: Calling this *empty* function is necessary to make cmds_user in this file to be kept in the final link.
|
|
//aos_cli_register_commands(cmds_user, sizeof(cmds_user)/sizeof(cmds_user[0]));
|
|
return 0;
|
|
}
|
|
|
|
#endif
|