diff --git a/doc/reference/networking/net_shell.rst b/doc/reference/networking/net_shell.rst index 40ccbfb46be83..d29c705d9a52a 100644 --- a/doc/reference/networking/net_shell.rst +++ b/doc/reference/networking/net_shell.rst @@ -31,7 +31,7 @@ The following net-shell commands are implemented: print more information if :option:`CONFIG_NET_BUF_POOL_USAGE` is set." "net nbr", "Print neighbor information. Only available if :option:`CONFIG_NET_IPV6` is set." - "net ping", "Ping a network host." + "net ping [-c count] [-i interval ms]", "Ping a network host." "net route", "Show IPv6 network routes. Only available if :option:`CONFIG_NET_ROUTE` is set." "net stats", "Show network statistics." diff --git a/samples/net/zperf/src/zperf_shell.c b/samples/net/zperf/src/zperf_shell.c index 00290364bf6dd..f2f30038e1a95 100644 --- a/samples/net/zperf/src/zperf_shell.c +++ b/samples/net/zperf/src/zperf_shell.c @@ -561,7 +561,7 @@ static int execute_upload(const struct shell *shell, * some time and start the test after that. */ net_icmpv6_send_echo_request(net_if_get_default(), - &ipv6->sin6_addr, 0, 0); + &ipv6->sin6_addr, 0, 0, NULL, 0); k_sleep(K_SECONDS(1)); } diff --git a/subsys/net/ip/icmpv4.c b/subsys/net/ip/icmpv4.c index c9973b8e2fdd7..b8f74e7656825 100644 --- a/subsys/net/ip/icmpv4.c +++ b/subsys/net/ip/icmpv4.c @@ -60,7 +60,8 @@ int net_icmpv4_finalize(struct net_pkt *pkt) } static enum net_verdict icmpv4_handle_echo_request(struct net_pkt *pkt, - struct net_ipv4_hdr *ip_hdr) + struct net_ipv4_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { struct net_pkt *reply = NULL; s16_t payload_len; @@ -128,7 +129,9 @@ static enum net_verdict icmpv4_handle_echo_request(struct net_pkt *pkt, int net_icmpv4_send_echo_request(struct net_if *iface, struct in_addr *dst, u16_t identifier, - u16_t sequence) + u16_t sequence, + const void *data, + size_t data_size) { NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access, struct net_icmpv4_echo_req); @@ -145,7 +148,8 @@ int net_icmpv4_send_echo_request(struct net_if *iface, src = &iface->config.ip.ipv4->unicast[0].address.in_addr; pkt = net_pkt_alloc_with_buffer(iface, - sizeof(struct net_icmpv4_echo_req), + sizeof(struct net_icmpv4_echo_req) + + data_size, AF_INET, IPPROTO_ICMP, PKT_WAIT_TIME); if (!pkt) { @@ -167,6 +171,7 @@ int net_icmpv4_send_echo_request(struct net_if *iface, echo_req->sequence = htons(sequence); net_pkt_set_data(pkt, &icmpv4_access); + net_pkt_write(pkt, data, data_size); net_pkt_cursor_init(pkt); @@ -319,7 +324,7 @@ enum net_verdict net_icmpv4_input(struct net_pkt *pkt, SYS_SLIST_FOR_EACH_CONTAINER(&handlers, cb, node) { if (cb->type == icmp_hdr->type && (cb->code == icmp_hdr->code || cb->code == 0U)) { - return cb->handler(pkt, ip_hdr); + return cb->handler(pkt, ip_hdr, icmp_hdr); } } diff --git a/subsys/net/ip/icmpv4.h b/subsys/net/ip/icmpv4.h index 683b7eb4729d9..774b2553129bf 100644 --- a/subsys/net/ip/icmpv4.h +++ b/subsys/net/ip/icmpv4.h @@ -34,7 +34,8 @@ struct net_icmpv4_echo_req { typedef enum net_verdict (*icmpv4_callback_handler_t)( struct net_pkt *pkt, - struct net_ipv4_hdr *ip_hdr); + struct net_ipv4_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr); struct net_icmpv4_handler { sys_snode_t node; @@ -61,13 +62,18 @@ int net_icmpv4_send_error(struct net_pkt *pkt, u8_t type, u8_t code); * to this Echo Request. May be zero. * @param sequence A sequence number to aid in matching Echo Replies * to this Echo Request. May be zero. + * @param data Arbitrary payload data that will be included in the + * Echo Reply verbatim. May be zero. + * @param data_size Size of the Payload Data in bytes. May be zero. * * @return Return 0 if the sending succeed, <0 otherwise. */ int net_icmpv4_send_echo_request(struct net_if *iface, struct in_addr *dst, u16_t identifier, - u16_t sequence); + u16_t sequence, + const void *data, + size_t data_size); void net_icmpv4_register_handler(struct net_icmpv4_handler *handler); diff --git a/subsys/net/ip/icmpv6.c b/subsys/net/ip/icmpv6.c index 8d9d0f7f8f3c7..fe78293f42fca 100644 --- a/subsys/net/ip/icmpv6.c +++ b/subsys/net/ip/icmpv6.c @@ -297,7 +297,9 @@ int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code, int net_icmpv6_send_echo_request(struct net_if *iface, struct in6_addr *dst, u16_t identifier, - u16_t sequence) + u16_t sequence, + const void *data, + size_t data_size) { NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv6_access, struct net_icmpv6_echo_req); @@ -309,7 +311,8 @@ int net_icmpv6_send_echo_request(struct net_if *iface, src = net_if_ipv6_select_src_addr(iface, dst); pkt = net_pkt_alloc_with_buffer(iface, - sizeof(struct net_icmpv6_echo_req), + sizeof(struct net_icmpv6_echo_req) + + data_size, AF_INET6, IPPROTO_ICMPV6, PKT_WAIT_TIME); if (!pkt) { @@ -331,6 +334,7 @@ int net_icmpv6_send_echo_request(struct net_if *iface, echo_req->sequence = htons(sequence); net_pkt_set_data(pkt, &icmpv6_access); + net_pkt_write(pkt, data, data_size); net_pkt_cursor_init(pkt); net_ipv6_finalize(pkt, IPPROTO_ICMPV6); diff --git a/subsys/net/ip/icmpv6.h b/subsys/net/ip/icmpv6.h index 5083f439c09b5..779a09d6d884e 100644 --- a/subsys/net/ip/icmpv6.h +++ b/subsys/net/ip/icmpv6.h @@ -178,13 +178,18 @@ int net_icmpv6_send_error(struct net_pkt *pkt, u8_t type, u8_t code, * to this Echo Request. May be zero. * @param sequence A sequence number to aid in matching Echo Replies * to this Echo Request. May be zero. + * @param data Arbitrary payload data that will be included in the + * Echo Reply verbatim. May be zero. + * @param data_size Size of the Payload Data in bytes. May be zero. * * @return Return 0 if the sending succeed, <0 otherwise. */ int net_icmpv6_send_echo_request(struct net_if *iface, struct in6_addr *dst, u16_t identifier, - u16_t sequence); + u16_t sequence, + const void *data, + size_t data_size); void net_icmpv6_register_handler(struct net_icmpv6_handler *handler); void net_icmpv6_unregister_handler(struct net_icmpv6_handler *handler); diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index f77cccbcb75ea..f72e398c93371 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -2659,24 +2659,60 @@ static enum net_verdict handle_ipv6_echo_reply(struct net_pkt *pkt, struct net_ipv6_hdr *ip_hdr, struct net_icmp_hdr *icmp_hdr) { - ARG_UNUSED(icmp_hdr); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmpv6_echo_req); + u32_t cycles; + struct net_icmpv6_echo_req *icmp_echo; - PR_SHELL(shell_for_ping, "Received echo reply from %s to %s\n", + icmp_echo = (struct net_icmpv6_echo_req *)net_pkt_get_data(pkt, &icmp_access); + if (icmp_echo == NULL) { + return -NET_DROP; + } + + net_pkt_skip(pkt, sizeof(*icmp_echo)); + if (net_pkt_read_be32(pkt, &cycles)) { + return -NET_DROP; + } + + cycles = k_cycle_get_32() - cycles; + + PR_SHELL(shell_for_ping, "%d bytes from %s to %s: icmp_seq=%d ttl=%d " +#ifdef CONFIG_IEEE802154 + "rssi=%d " +#endif +#ifdef CONFIG_FLOAT + "time=%.2f ms\n", +#else + "time=%d ms\n", +#endif + ntohs(ip_hdr->len) - net_pkt_ipv6_ext_len(pkt) - NET_ICMPH_LEN, net_sprint_ipv6_addr(&ip_hdr->src), - net_sprint_ipv6_addr(&ip_hdr->dst)); + net_sprint_ipv6_addr(&ip_hdr->dst), + ntohs(icmp_echo->sequence), + ip_hdr->hop_limit, +#ifdef CONFIG_IEEE802154 + net_pkt_ieee802154_rssi(pkt), +#endif +#ifdef CONFIG_FLOAT + (SYS_CLOCK_HW_CYCLES_TO_NS(cycles) / 1000000.f)); +#else + (SYS_CLOCK_HW_CYCLES_TO_NS(cycles) / 1000000)); +#endif k_sem_give(&ping_timeout); - remove_ipv6_ping_handler(); net_pkt_unref(pkt); return NET_OK; } -static int ping_ipv6(const struct shell *shell, char *host) +static int ping_ipv6(const struct shell *shell, + char *host, + unsigned int count, + unsigned int interval) { struct in6_addr ipv6_target; struct net_if *iface = net_if_get_default(); struct net_nbr *nbr; - int ret; + int ret = 0; #if defined(CONFIG_NET_ROUTE) struct net_route_entry *route; @@ -2700,16 +2736,26 @@ static int ping_ipv6(const struct shell *shell, char *host) } #endif - ret = net_icmpv6_send_echo_request(iface, - &ipv6_target, - sys_rand32_get(), - sys_rand32_get()); - if (ret) { - remove_ipv6_ping_handler(); - } else { - PR("Sent a ping to %s\n", host); + PR("PING %s\n", host); + + for (int i = 0; i < count; ++i) { + u32_t time_stamp = htonl(k_cycle_get_32()); + + ret = net_icmpv6_send_echo_request(iface, + &ipv6_target, + sys_rand32_get(), + i, + &time_stamp, + sizeof(time_stamp)); + if (ret) { + break; + } + + k_sleep(interval); } + remove_ipv6_ping_handler(); + return ret; } #else @@ -2720,7 +2766,8 @@ static int ping_ipv6(const struct shell *shell, char *host) #if defined(CONFIG_NET_IPV4) static enum net_verdict handle_ipv4_echo_reply(struct net_pkt *pkt, - struct net_ipv4_hdr *ip_hdr); + struct net_ipv4_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr); static struct net_icmpv4_handler ping4_handler = { .type = NET_ICMPV4_ECHO_REPLY, @@ -2734,46 +2781,114 @@ static inline void remove_ipv4_ping_handler(void) } static enum net_verdict handle_ipv4_echo_reply(struct net_pkt *pkt, - struct net_ipv4_hdr *ip_hdr) + struct net_ipv4_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - PR_SHELL(shell_for_ping, "Received echo reply from %s to %s\n", + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmpv4_echo_req); + u32_t cycles; + struct net_icmpv4_echo_req *icmp_echo; + + icmp_echo = (struct net_icmpv4_echo_req *)net_pkt_get_data(pkt, &icmp_access); + if (icmp_echo == NULL) { + return -NET_DROP; + } + + net_pkt_skip(pkt, sizeof(*icmp_echo)); + if (net_pkt_read_be32(pkt, &cycles)) { + return -NET_DROP; + } + + cycles = k_cycle_get_32() - cycles; + + PR_SHELL(shell_for_ping, "%d bytes from %s to %s: icmp_seq=%d ttl=%d " +#ifdef CONFIG_FLOAT + "time=%.2f ms\n", +#else + "time=%d ms\n", +#endif + ntohs(ip_hdr->len) - net_pkt_ipv6_ext_len(pkt) - NET_ICMPH_LEN, net_sprint_ipv4_addr(&ip_hdr->src), - net_sprint_ipv4_addr(&ip_hdr->dst)); + net_sprint_ipv4_addr(&ip_hdr->dst), + ntohs(icmp_echo->sequence), + ip_hdr->ttl, +#ifdef CONFIG_FLOAT + (SYS_CLOCK_HW_CYCLES_TO_NS(cycles) / 1000000.f)); +#else + (SYS_CLOCK_HW_CYCLES_TO_NS(cycles) / 1000000)); +#endif k_sem_give(&ping_timeout); - remove_ipv4_ping_handler(); net_pkt_unref(pkt); return NET_OK; } -static int ping_ipv4(const struct shell *shell, char *host) +static int ping_ipv4(const struct shell *shell, + char *host, + unsigned int count, + unsigned int interval) { struct in_addr ipv4_target; - int ret; + int ret = 0; if (net_addr_pton(AF_INET, host, &ipv4_target) < 0) { return -EINVAL; } + struct net_if *iface = net_if_ipv4_select_src_iface(&ipv4_target); + net_icmpv4_register_handler(&ping4_handler); - ret = net_icmpv4_send_echo_request( - net_if_ipv4_select_src_iface(&ipv4_target), - &ipv4_target, - sys_rand32_get(), - sys_rand32_get()); - if (ret) { - remove_ipv4_ping_handler(); - } else { - PR("Sent a ping to %s\n", host); + PR("PING %s\n", host); + + for (int i = 0; i < count; ++i) { + u32_t time_stamp = htonl(k_cycle_get_32()); + + ret = net_icmpv4_send_echo_request(iface, + &ipv4_target, + sys_rand32_get(), + i, + &time_stamp, + sizeof(time_stamp)); + if (ret) { + break; + } + + k_sleep(interval); } + remove_ipv4_ping_handler(); + return ret; } #else #define ping_ipv4(...) -ENOTSUP #define remove_ipv4_ping_handler() #endif /* CONFIG_NET_IPV4 */ + +static int _parse_arg(size_t *i, size_t argc, char *argv[]) +{ + int res = -1; + const char *str = argv[*i] + 2; + char *endptr; + + if (*str == 0) { + if (*i + 1 >= argc) { + return -1; + } + + *i += 1; + str = argv[*i]; + } + + res = strtol(str, &endptr, 10); + + if (errno || (endptr == str)) { + return -1; + } + + return res; +} #endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */ static int cmd_net_ping(const struct shell *shell, size_t argc, char *argv[]) @@ -2785,12 +2900,42 @@ static int cmd_net_ping(const struct shell *shell, size_t argc, char *argv[]) return -EOPNOTSUPP; #else - char *host; + char *host = NULL; int ret; - ARG_UNUSED(argc); + int count = 3; + int interval = 1000; + + for (size_t i = 1; i < argc; ++i) { + + if (*argv[i] != '-') { + host = argv[i]; + continue; + } + + switch (argv[i][1]) { + case 'c': + count = _parse_arg(&i, argc, argv); + if (count < 0) { + PR_WARNING("Parse error: %s\n", argv[i]); + return -ENOEXEC; + } - host = argv[1]; + + break; + case 'i': + interval = _parse_arg(&i, argc, argv); + if (interval < 0) { + PR_WARNING("Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + + break; + default: + PR_WARNING("Unrecognized argument: %s\n", argv[i]); + return -ENOEXEC; + } + } if (!host) { PR_WARNING("Target host missing\n"); @@ -2800,7 +2945,7 @@ static int cmd_net_ping(const struct shell *shell, size_t argc, char *argv[]) shell_for_ping = shell; if (IS_ENABLED(CONFIG_NET_IPV6)) { - ret = ping_ipv6(shell, host); + ret = ping_ipv6(shell, host, count, interval); if (!ret) { goto wait_reply; } else if (ret == -EIO) { @@ -2810,7 +2955,7 @@ static int cmd_net_ping(const struct shell *shell, size_t argc, char *argv[]) } if (IS_ENABLED(CONFIG_NET_IPV4)) { - ret = ping_ipv4(shell, host); + ret = ping_ipv4(shell, host, count, interval); if (ret) { if (ret == -EIO) { PR_WARNING("Cannot send IPv4 ping\n"); @@ -3783,7 +3928,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_commands, cmd_net_mem), SHELL_CMD(nbr, &net_cmd_nbr, "Print neighbor information.", cmd_net_nbr), - SHELL_CMD(ping, NULL, "Ping a network host.", cmd_net_ping), + SHELL_CMD(ping, NULL, "'net ping [-c count] [-i interval ms] ' ping a network host.", cmd_net_ping), SHELL_CMD(route, NULL, "Show network route.", cmd_net_route), SHELL_CMD(stacks, NULL, "Show network stacks information.", cmd_net_stacks),