net: ping6: Add ping6 command
authorViacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
Fri, 2 Dec 2022 09:18:08 +0000 (12:18 +0300)
committerTom Rini <trini@konsulko.com>
Mon, 5 Dec 2022 17:47:16 +0000 (12:47 -0500)
Implement ping6 command to ping hosts using IPv6. It works the same way as
an ordinary ping command. There is no ICMP request so it is not possible
to ping our host. This patch adds options in Kconfig and Makefile to
build ping6 command.

Series-changes: 3
- Added structures and functions descriptions
- Added to ping6_receive() return value instead of void

Series-changes: 4
- Fixed structures and functions description style

Signed-off-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
cmd/Kconfig
cmd/net.c
include/net.h
include/net6.h
net/Makefile
net/net.c
net/net6.c
net/ping6.c [new file with mode: 0644]

index a4b6fc6a5da03da6796c2184bed28ad2dc1003ff..d93731f2af686cc5c7786e82efbd3bab2b41e7c8 100644 (file)
@@ -1847,6 +1847,13 @@ config CMD_PING
        help
          Send ICMP ECHO_REQUEST to network host
 
+config CMD_PING6
+       bool "ping6"
+       depends on IPV6
+       default y if (CMD_PING && IPV6)
+       help
+         Send ICMPv6 ECHO_REQUEST to network host
+
 config CMD_CDP
        bool "cdp"
        help
index 221b3525caa30686edbb27c372dddc0d25aff265..0e9f200ca97e6959cfb1985c0e1af0f9302a2ee3 100644 (file)
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -403,6 +403,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], strlen(argv[1]), &net_ping_ip6))
+               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)
index e0c7804827416a82414ea93def7617134880f478..1a99009959de1dd1cf41cf2a9787d74c1ce21687 100644 (file)
@@ -560,8 +560,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, NCSI, WGET
+       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
+       SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
 };
 
 extern char    net_boot_file_name[1024];/* Boot File name */
index 622885e7df842f17d51754dd196cc9bd14ef3d3c..9b3de028e6dcf06af798076dd788dbffb4afda4d 100644 (file)
@@ -172,6 +172,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;
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -403,4 +404,29 @@ static inline void net_copy_ip6(void *to, const void *from)
 }
 #endif
 
+#if IS_ENABLED(CONFIG_CMD_PING6)
+/* Send ping requset */
+void ping6_start(void);
+
+/**
+ * ping6_receive() - Handle reception of ICMPv6 echo request/reply
+ *
+ * @et:                pointer to incoming patcket
+ * @ip6:       pointer to IPv6 protocol
+ * @len:       packet length
+ * Return: 0 if success, -EINVAL in case of failure during reception
+ */
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+#else
+static inline void ping6_start(void)
+{
+}
+
+static inline
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+       return -EINVAL;
+}
+#endif /* CONFIG_CMD_PING6 */
+
 #endif /* __NET6_H__ */
index df0ea500feadbae9c2aa6492d2cae607d830c913..13eef04029da026a22f806e2783e62267d046d97 100644 (file)
@@ -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
index 28c8dfdef23fce47ac95cf5c5c8f937a7a06278d..1c39acc4936c4669546f8936e64cf519c76008bb 100644 (file)
--- a/net/net.c
+++ b/net/net.c
@@ -525,6 +525,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();
@@ -1434,6 +1439,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) {
index e604227fe5679d0c86cb0d37485e0449e6906cab..fdea0787885e48e1a7ccdee97743df43620a8aeb 100644 (file)
@@ -404,6 +404,10 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
                        return -EINVAL;
 
                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 (file)
index 0000000..4882a17
--- /dev/null
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@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;
+       u16 csum_p;
+       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, PROT_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 */
+       csum_p = csum_partial((u8 *)msg, len, 0);
+       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
+                                                PROT_ICMPV6, csum_p);
+
+       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();
+}
+
+int 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 -EINVAL;
+               net_set_state(NETLOOP_SUCCESS);
+               break;
+       case IPV6_ICMP_ECHO_REQUEST:
+               /* ignore for now.... */
+               debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
+               return -EINVAL;
+       default:
+               debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}