1258945Sroberto// SPDX-License-Identifier: GPL-2.0+
2258945Sroberto/*
3258945Sroberto * Copyright (C) 2013 Allied Telesis Labs NZ
4258945Sroberto * Chris Packham, <judge.packham@gmail.com>
5258945Sroberto *
6258945Sroberto * Copyright (C) 2022 YADRO
7258945Sroberto * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
8258945Sroberto */
9258945Sroberto
10258945Sroberto/* Simple ping6 implementation */
11258945Sroberto
12258945Sroberto#include <common.h>
13258945Sroberto#include <net.h>
14258945Sroberto#include <net6.h>
15258945Sroberto#include "ndisc.h"
16258945Sroberto
17258945Srobertostatic ushort seq_no;
18258945Sroberto
19258945Sroberto/* the ipv6 address to ping */
20258945Srobertostruct in6_addr net_ping_ip6;
21258945Sroberto
22258945Srobertoint
23258945Srobertoip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
24258945Sroberto{
25258945Sroberto	struct echo_msg *msg;
26258945Sroberto	u16 len;
27258945Sroberto	u16 csum_p;
28258945Sroberto	uchar *pkt_old = pkt;
29258945Sroberto
30258945Sroberto	len = sizeof(struct echo_msg);
31258945Sroberto
32258945Sroberto	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
33258945Sroberto	pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, PROT_ICMPV6,
34258945Sroberto			   IPV6_NDISC_HOPLIMIT, len);
35258945Sroberto
36258945Sroberto	/* ICMPv6 - Echo */
37258945Sroberto	msg = (struct echo_msg *)pkt;
38258945Sroberto	msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
39258945Sroberto	msg->icmph.icmp6_code = 0;
40258945Sroberto	msg->icmph.icmp6_cksum = 0;
41258945Sroberto	msg->icmph.icmp6_identifier = 0;
42258945Sroberto	msg->icmph.icmp6_sequence = htons(seq_no++);
43258945Sroberto	msg->id = msg->icmph.icmp6_identifier;	/* these seem redundant */
44258945Sroberto	msg->sequence = msg->icmph.icmp6_sequence;
45258945Sroberto
46258945Sroberto	/* checksum */
47258945Sroberto	csum_p = csum_partial((u8 *)msg, len, 0);
48258945Sroberto	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
49258945Sroberto						 PROT_ICMPV6, csum_p);
50258945Sroberto
51258945Sroberto	pkt += len;
52258945Sroberto
53258945Sroberto	return pkt - pkt_old;
54258945Sroberto}
55258945Sroberto
56258945Srobertoint ping6_send(void)
57258945Sroberto{
58258945Sroberto	uchar *pkt;
59258945Sroberto	static uchar mac[6];
60258945Sroberto
61258945Sroberto	/* always send neighbor solicit */
62258945Sroberto
63258945Sroberto	memcpy(mac, net_null_ethaddr, 6);
64258945Sroberto
65258945Sroberto	net_nd_sol_packet_ip6 = net_ping_ip6;
66258945Sroberto	net_nd_packet_mac = mac;
67258945Sroberto
68258945Sroberto	pkt = net_nd_tx_packet;
69258945Sroberto	pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
70258945Sroberto
71258945Sroberto	/* size of the waiting packet */
72258945Sroberto	net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
73258945Sroberto
74258945Sroberto	/* and do the ARP request */
75258945Sroberto	net_nd_try = 1;
76258945Sroberto	net_nd_timer_start = get_timer(0);
77258945Sroberto	ndisc_request();
78258945Sroberto	return 1;		/* waiting */
79258945Sroberto}
80258945Sroberto
81258945Srobertostatic void ping6_timeout(void)
82258945Sroberto{
83258945Sroberto	eth_halt();
84258945Sroberto	net_set_state(NETLOOP_FAIL);	/* we did not get the reply */
85258945Sroberto}
86258945Sroberto
87258945Srobertovoid ping6_start(void)
88258945Sroberto{
89258945Sroberto	printf("Using %s device\n", eth_get_name());
90258945Sroberto	net_set_timeout_handler(10000UL, ping6_timeout);
91258945Sroberto
92258945Sroberto	ping6_send();
93258945Sroberto}
94258945Sroberto
95258945Srobertoint ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
96258945Sroberto{
97258945Sroberto	struct icmp6hdr *icmp =
98258945Sroberto	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
99258945Sroberto	struct in6_addr src_ip;
100258945Sroberto
101258945Sroberto	switch (icmp->icmp6_type) {
102258945Sroberto	case IPV6_ICMP_ECHO_REPLY:
103258945Sroberto		src_ip = ip6->saddr;
104258945Sroberto		if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)))
105258945Sroberto			return -EINVAL;
106258945Sroberto		net_set_state(NETLOOP_SUCCESS);
107258945Sroberto		break;
108258945Sroberto	case IPV6_ICMP_ECHO_REQUEST:
109258945Sroberto		/* ignore for now.... */
110258945Sroberto		debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
111258945Sroberto		return -EINVAL;
112258945Sroberto	default:
113258945Sroberto		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
114258945Sroberto		return -EINVAL;
115258945Sroberto	}
116258945Sroberto
117258945Sroberto	return 0;
118258945Sroberto}
119258945Sroberto