1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2013 Allied Telesis Labs NZ
4  * Chris Packham, <judge.packham@gmail.com>
5  *
6  * Copyright (C) 2022 YADRO
7  * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
8  */
9 
10 /* Simple ping6 implementation */
11 
12 #include <net.h>
13 #include <net6.h>
14 #include "ndisc.h"
15 
16 static ushort seq_no;
17 
18 /* the ipv6 address to ping */
19 struct in6_addr net_ping_ip6;
20 
21 int
ip6_make_ping(uchar * eth_dst_addr,struct in6_addr * neigh_addr,uchar * pkt)22 ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
23 {
24 	struct echo_msg *msg;
25 	u16 len;
26 	u16 csum_p;
27 	uchar *pkt_old = pkt;
28 
29 	len = sizeof(struct echo_msg);
30 
31 	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
32 	pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, PROT_ICMPV6,
33 			   IPV6_NDISC_HOPLIMIT, len);
34 
35 	/* ICMPv6 - Echo */
36 	msg = (struct echo_msg *)pkt;
37 	msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
38 	msg->icmph.icmp6_code = 0;
39 	msg->icmph.icmp6_cksum = 0;
40 	msg->icmph.icmp6_identifier = 0;
41 	msg->icmph.icmp6_sequence = htons(seq_no++);
42 	msg->id = msg->icmph.icmp6_identifier;	/* these seem redundant */
43 	msg->sequence = msg->icmph.icmp6_sequence;
44 
45 	/* checksum */
46 	csum_p = csum_partial((u8 *)msg, len, 0);
47 	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
48 						 PROT_ICMPV6, csum_p);
49 
50 	pkt += len;
51 
52 	return pkt - pkt_old;
53 }
54 
ping6_send(void)55 int ping6_send(void)
56 {
57 	uchar *pkt;
58 	static uchar mac[6];
59 
60 	/* always send neighbor solicit */
61 
62 	memcpy(mac, net_null_ethaddr, 6);
63 
64 	net_nd_sol_packet_ip6 = net_ping_ip6;
65 	net_nd_packet_mac = mac;
66 
67 	pkt = net_nd_tx_packet;
68 	pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
69 
70 	/* size of the waiting packet */
71 	net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
72 
73 	/* and do the ARP request */
74 	net_nd_try = 1;
75 	net_nd_timer_start = get_timer(0);
76 	ndisc_request();
77 	return 1;		/* waiting */
78 }
79 
ping6_timeout(void)80 static void ping6_timeout(void)
81 {
82 	eth_halt();
83 	net_set_state(NETLOOP_FAIL);	/* we did not get the reply */
84 }
85 
ping6_start(void)86 void ping6_start(void)
87 {
88 	printf("Using %s device\n", eth_get_name());
89 	net_set_timeout_handler(10000UL, ping6_timeout);
90 
91 	ping6_send();
92 }
93 
ping6_receive(struct ethernet_hdr * et,struct ip6_hdr * ip6,int len)94 int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
95 {
96 	struct icmp6hdr *icmp =
97 	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
98 	struct in6_addr src_ip;
99 
100 	switch (icmp->icmp6_type) {
101 	case IPV6_ICMP_ECHO_REPLY:
102 		src_ip = ip6->saddr;
103 		if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)))
104 			return -EINVAL;
105 		net_set_state(NETLOOP_SUCCESS);
106 		break;
107 	case IPV6_ICMP_ECHO_REQUEST:
108 		/* ignore for now.... */
109 		debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
110 		return -EINVAL;
111 	default:
112 		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
113 		return -EINVAL;
114 	}
115 
116 	return 0;
117 }
118