1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2022 Linaro
4 *
5 * (C) Copyright 2022
6 * Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
7 */
8
9#include <common.h>
10#include <command.h>
11#include <dm.h>
12#include <env.h>
13#include <fdtdec.h>
14#include <log.h>
15#include <malloc.h>
16#include <net.h>
17#include <net/tcp.h>
18#include <net/wget.h>
19#include <asm/eth.h>
20#include <dm/test.h>
21#include <dm/device-internal.h>
22#include <dm/uclass-internal.h>
23#include <test/lib.h>
24#include <test/test.h>
25#include <test/ut.h>
26
27#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
28#define LEN_B_TO_DW(x) ((x) >> 2)
29
30static int sb_arp_handler(struct udevice *dev, void *packet,
31			  unsigned int len)
32{
33	struct eth_sandbox_priv *priv = dev_get_priv(dev);
34	struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
35	int ret = 0;
36
37	if (ntohs(arp->ar_op) == ARPOP_REQUEST) {
38		priv->fake_host_ipaddr = net_read_ip(&arp->ar_spa);
39
40		ret = sandbox_eth_recv_arp_req(dev);
41		if (ret)
42			return ret;
43		ret = sandbox_eth_arp_req_to_reply(dev, packet, len);
44		return ret;
45	}
46
47	return -EPROTONOSUPPORT;
48}
49
50static int sb_syn_handler(struct udevice *dev, void *packet,
51			  unsigned int len)
52{
53	struct eth_sandbox_priv *priv = dev_get_priv(dev);
54	struct ethernet_hdr *eth = packet;
55	struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
56	struct ethernet_hdr *eth_send;
57	struct ip_tcp_hdr *tcp_send;
58
59	/* Don't allow the buffer to overrun */
60	if (priv->recv_packets >= PKTBUFSRX)
61		return 0;
62
63	eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
64	memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
65	memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
66	eth_send->et_protlen = htons(PROT_IP);
67	tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
68	tcp_send->tcp_src = tcp->tcp_dst;
69	tcp_send->tcp_dst = tcp->tcp_src;
70	tcp_send->tcp_seq = htonl(0);
71	tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
72	tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
73	tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
74	tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
75	tcp_send->tcp_xsum = 0;
76	tcp_send->tcp_ugr = 0;
77	tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
78						   tcp->ip_src,
79						   tcp->ip_dst,
80						   TCP_HDR_SIZE,
81						   IP_TCP_HDR_SIZE);
82	net_set_ip_header((uchar *)tcp_send,
83			  tcp->ip_src,
84			  tcp->ip_dst,
85			  IP_TCP_HDR_SIZE,
86			  IPPROTO_TCP);
87
88	priv->recv_packet_length[priv->recv_packets] =
89		ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
90	++priv->recv_packets;
91
92	return 0;
93}
94
95static int sb_ack_handler(struct udevice *dev, void *packet,
96			  unsigned int len)
97{
98	struct eth_sandbox_priv *priv = dev_get_priv(dev);
99	struct ethernet_hdr *eth = packet;
100	struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
101	struct ethernet_hdr *eth_send;
102	struct ip_tcp_hdr *tcp_send;
103	void *data;
104	int pkt_len;
105	int payload_len = 0;
106	const char *payload1 = "HTTP/1.1 200 OK\r\n"
107		"Content-Length: 30\r\n\r\n\r\n"
108		"<html><body>Hi</body></html>\r\n";
109
110	/* Don't allow the buffer to overrun */
111	if (priv->recv_packets >= PKTBUFSRX)
112		return 0;
113
114	eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
115	memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
116	memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
117	eth_send->et_protlen = htons(PROT_IP);
118	tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
119	tcp_send->tcp_src = tcp->tcp_dst;
120	tcp_send->tcp_dst = tcp->tcp_src;
121	data = (void *)tcp_send + IP_TCP_HDR_SIZE;
122
123	if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) {
124		tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
125		tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
126		payload_len = strlen(payload1);
127		memcpy(data, payload1, payload_len);
128		tcp_send->tcp_flags = TCP_ACK;
129	} else if (ntohl(tcp->tcp_seq) == 2) {
130		tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
131		tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
132		payload_len = 0;
133		tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
134	}
135
136	tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
137	tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
138	tcp_send->tcp_xsum = 0;
139	tcp_send->tcp_ugr = 0;
140	pkt_len = IP_TCP_HDR_SIZE + payload_len;
141	tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
142						   tcp->ip_src,
143						   tcp->ip_dst,
144						   pkt_len - IP_HDR_SIZE,
145						   pkt_len);
146	net_set_ip_header((uchar *)tcp_send,
147			  tcp->ip_src,
148			  tcp->ip_dst,
149			  pkt_len,
150			  IPPROTO_TCP);
151
152	if (ntohl(tcp->tcp_seq) == 1 || ntohl(tcp->tcp_seq) == 2) {
153		priv->recv_packet_length[priv->recv_packets] =
154			ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
155		++priv->recv_packets;
156	}
157
158	return 0;
159}
160
161static int sb_http_handler(struct udevice *dev, void *packet,
162			   unsigned int len)
163{
164	struct ethernet_hdr *eth = packet;
165	struct ip_hdr *ip;
166	struct ip_tcp_hdr *tcp;
167
168	if (ntohs(eth->et_protlen) == PROT_ARP) {
169		return sb_arp_handler(dev, packet, len);
170	} else if (ntohs(eth->et_protlen) == PROT_IP) {
171		ip = packet + ETHER_HDR_SIZE;
172		if (ip->ip_p == IPPROTO_TCP) {
173			tcp = packet + ETHER_HDR_SIZE;
174			if (tcp->tcp_flags == TCP_SYN)
175				return sb_syn_handler(dev, packet, len);
176			else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
177				return sb_ack_handler(dev, packet, len);
178			return 0;
179		}
180		return -EPROTONOSUPPORT;
181	}
182
183	return -EPROTONOSUPPORT;
184}
185
186static int net_test_wget(struct unit_test_state *uts)
187{
188	sandbox_eth_set_tx_handler(0, sb_http_handler);
189	sandbox_eth_set_priv(0, uts);
190
191	env_set("ethact", "eth@10002000");
192	env_set("ethrotate", "no");
193	env_set("loadaddr", "0x20000");
194	ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0));
195
196	sandbox_eth_set_tx_handler(0, NULL);
197
198	ut_assertok(console_record_reset_enable());
199	run_command("md5sum ${loadaddr} ${filesize}", 0);
200	ut_assert_nextline("md5 for 00020000 ... 0002001f ==> 234af48e94b0085060249ecb5942ab57");
201	ut_assertok(ut_check_console_end(uts));
202
203	return 0;
204}
205
206LIB_TEST(net_test_wget, 0);
207