Implement ping6 command to ping hosts using IPv6. There is no ICMP request so it is not possible to ping our host.
Signed-off-by: Viacheslav Mitrofanov <v.v.mitrofa...@yadro.com> --- cmd/Kconfig | 6 +++ cmd/net.c | 26 +++++++++++ include/net.h | 4 +- include/net6.h | 17 +++++++ net/Makefile | 1 + net/net.c | 13 ++++++ net/net6.c | 4 ++ net/ping6.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 net/ping6.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 211ebe9c87..e8f4e43ebe 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1801,6 +1801,12 @@ config CMD_PING help Send ICMP ECHO_REQUEST to network host +config CMD_PING6 + bool "ping6" + depends on IPV6 + help + Send ICMPv6 ECHO_REQUEST to network host + config CMD_CDP bool "cdp" help diff --git a/cmd/net.c b/cmd/net.c index 9225bddf6b..7ff1efec1f 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -333,6 +333,32 @@ U_BOOT_CMD( ); #endif +#if IS_ENABLED(CONFIG_CMD_PING6) +int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + if (string_to_ip6(argv[1], &net_ping_ip6) != 0) + return CMD_RET_USAGE; + + use_ip6 = true; + if (net_loop(PING6) < 0) { + use_ip6 = false; + printf("ping6 failed; host %pI6c is not alive\n", + &net_ping_ip6); + return 1; + } + + use_ip6 = false; + printf("host %pI6c is alive\n", &net_ping_ip6); + return 0; +} + +U_BOOT_CMD( + ping6, 2, 1, do_ping6, + "send ICMPv6 ECHO_REQUEST to network host", + "pingAddress" +); +#endif /* CONFIG_CMD_PING6 */ + #if defined(CONFIG_CMD_CDP) static void cdp_update_env(void) diff --git a/include/net.h b/include/net.h index c06b577808..72d32d358a 100644 --- a/include/net.h +++ b/include/net.h @@ -559,8 +559,8 @@ extern ushort net_native_vlan; /* Our Native VLAN */ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { - BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP, - TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP + BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS, + SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP }; extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/include/net6.h b/include/net6.h index 383f8336e7..6f0bd626ee 100644 --- a/include/net6.h +++ b/include/net6.h @@ -175,6 +175,7 @@ extern struct in6_addr net_ip6; /* Our IPv6 addr (0 = unknown) */ extern struct in6_addr net_link_local_ip6; /* Our link local IPv6 addr */ extern u32 net_prefix_length; /* Our prefixlength (0 = unknown) */ extern struct in6_addr net_server_ip6; /* Server IPv6 addr (0 = unknown) */ +extern struct in6_addr net_ping_ip6; /* the ipv6 address to ping */ extern bool use_ip6; #ifdef CONFIG_IPV6 @@ -292,4 +293,20 @@ static inline void net_copy_ip6(void *to, const void *from) } #endif +#if IS_ENABLED(CONFIG_CMD_PING6) +/* starts a Ping6 process */ +void ping6_start(void); +/* handles reception of icmpv6 echo request/reply */ +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len); +#else +static inline void ping6_start(void) +{ +} + +static inline +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) +{ +} +#endif /* CONFIG_CMD_PING6 */ + #endif /* __NET6_H__ */ diff --git a/net/Makefile b/net/Makefile index 766dd04135..e32e913d68 100644 --- a/net/Makefile +++ b/net/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_NET) += net.o obj-$(CONFIG_IPV6) += net6.o obj-$(CONFIG_CMD_NFS) += nfs.o obj-$(CONFIG_CMD_PING) += ping.o +obj-$(CONFIG_CMD_PING6) += ping6.o obj-$(CONFIG_CMD_PCAP) += pcap.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o diff --git a/net/net.c b/net/net.c index d519173101..e5fcbcaec7 100644 --- a/net/net.c +++ b/net/net.c @@ -509,6 +509,11 @@ restart: ping_start(); break; #endif +#if defined(CONFIG_CMD_PING6) + case PING6: + ping6_start(); + break; +#endif #if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD) case NFS: nfs_start(); @@ -1360,6 +1365,14 @@ static int net_check_prereq(enum proto_t protocol) } goto common; #endif +#if defined(CONFIG_CMD_PING6) + case PING6: + if (ip6_is_unspecified_addr(&net_ping_ip6)) { + puts("*** ERROR: ping address not given\n"); + return 1; + } + goto common; +#endif #if defined(CONFIG_CMD_DNS) case DNS: if (net_dns_server.s_addr == 0) { diff --git a/net/net6.c b/net/net6.c index 0799d411b2..a3b89ded24 100644 --- a/net/net6.c +++ b/net/net6.c @@ -444,6 +444,10 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) return; switch (icmp->icmp6_type) { + case IPV6_ICMP_ECHO_REQUEST: + case IPV6_ICMP_ECHO_REPLY: + ping6_receive(et, ip6, len); + break; case IPV6_NDISC_NEIGHBOUR_SOLICITATION: case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT: ndisc_receive(et, ip6, len); diff --git a/net/ping6.c b/net/ping6.c new file mode 100644 index 0000000000..3284e314ae --- /dev/null +++ b/net/ping6.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Allied Telesis Labs NZ + * Chris Packham, <judge.pack...@gmail.com> + * + * Copyright (C) 2022 YADRO + * Viacheslav Mitrofanov <v.v.mitrofa...@yadro.com> + */ + +/* + * Simple ping6 implementation + */ + +#include <common.h> +#include <net.h> +#include <net6.h> +#include "ndisc.h" + +static ushort seq_no; + +/* the ipv6 address to ping */ +struct in6_addr net_ping_ip6; + +int +ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt) +{ + struct echo_msg *msg; + __u16 len; + uchar *pkt_old = pkt; + + len = sizeof(struct echo_msg); + + pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6); + pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, IPPROTO_ICMPV6, + IPV6_NDISC_HOPLIMIT, len); + + /* ICMPv6 - Echo */ + msg = (struct echo_msg *)pkt; + msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST; + msg->icmph.icmp6_code = 0; + msg->icmph.icmp6_cksum = 0; + msg->icmph.icmp6_identifier = 0; + msg->icmph.icmp6_sequence = htons(seq_no++); + msg->id = msg->icmph.icmp6_identifier; /* these seem redundant */ + msg->sequence = msg->icmph.icmp6_sequence; + + /* checksum */ + msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len, + IPPROTO_ICMPV6, + csum_partial((u8 *)msg, + len, 0)); + + pkt += len; + + return pkt - pkt_old; +} + +int ping6_send(void) +{ + uchar *pkt; + static uchar mac[6]; + + /* always send neighbor solicit */ + + memcpy(mac, net_null_ethaddr, 6); + + net_nd_sol_packet_ip6 = net_ping_ip6; + net_nd_packet_mac = mac; + + pkt = net_nd_tx_packet; + pkt += ip6_make_ping(mac, &net_ping_ip6, pkt); + + /* size of the waiting packet */ + net_nd_tx_packet_size = (pkt - net_nd_tx_packet); + + /* and do the ARP request */ + net_nd_try = 1; + net_nd_timer_start = get_timer(0); + ndisc_request(); + return 1; /* waiting */ +} + +static void ping6_timeout(void) +{ + eth_halt(); + net_set_state(NETLOOP_FAIL); /* we did not get the reply */ +} + +void ping6_start(void) +{ + printf("Using %s device\n", eth_get_name()); + net_set_timeout_handler(10000UL, ping6_timeout); + + ping6_send(); +} + +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) +{ + struct icmp6hdr *icmp = + (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); + struct in6_addr src_ip; + + switch (icmp->icmp6_type) { + case IPV6_ICMP_ECHO_REPLY: + src_ip = ip6->saddr; + if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr))) + return; + net_set_state(NETLOOP_SUCCESS); + break; + case IPV6_ICMP_ECHO_REQUEST: + debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr); + /* ignore for now.... */ + break; + default: + debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type); + } +} -- 2.25.1