blob: d51650d5854639c3ceca679296116e6eea2635bd [file] [log] [blame]
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +08001// 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
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +08009#include <command.h>
10#include <dm.h>
11#include <env.h>
12#include <fdtdec.h>
13#include <log.h>
14#include <malloc.h>
15#include <net.h>
16#include <net/tcp.h>
17#include <net/wget.h>
18#include <asm/eth.h>
19#include <dm/test.h>
20#include <dm/device-internal.h>
21#include <dm/uclass-internal.h>
Jerome Forissier97871922024-11-15 17:45:14 +010022#include <test/cmd.h>
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +080023#include <test/test.h>
24#include <test/ut.h>
25
26#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
27#define LEN_B_TO_DW(x) ((x) >> 2)
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +030028#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +090029
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +080030static 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);
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +030066 priv->irs = ntohl(tcp->tcp_seq);
67 priv->iss = ~priv->irs; /* just to differ from irs */
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +080068 eth_send->et_protlen = htons(PROT_IP);
69 tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
70 tcp_send->tcp_src = tcp->tcp_dst;
71 tcp_send->tcp_dst = tcp->tcp_src;
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +030072 tcp_send->tcp_seq = htonl(priv->iss);
73 tcp_send->tcp_ack = htonl(priv->irs + 1);
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +080074 tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
75 tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
76 tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
77 tcp_send->tcp_xsum = 0;
78 tcp_send->tcp_ugr = 0;
79 tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
80 tcp->ip_src,
81 tcp->ip_dst,
82 TCP_HDR_SIZE,
83 IP_TCP_HDR_SIZE);
84 net_set_ip_header((uchar *)tcp_send,
85 tcp->ip_src,
86 tcp->ip_dst,
87 IP_TCP_HDR_SIZE,
88 IPPROTO_TCP);
89
90 priv->recv_packet_length[priv->recv_packets] =
91 ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
92 ++priv->recv_packets;
93
94 return 0;
95}
96
97static int sb_ack_handler(struct udevice *dev, void *packet,
98 unsigned int len)
99{
100 struct eth_sandbox_priv *priv = dev_get_priv(dev);
101 struct ethernet_hdr *eth = packet;
102 struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
103 struct ethernet_hdr *eth_send;
104 struct ip_tcp_hdr *tcp_send;
105 void *data;
106 int pkt_len;
107 int payload_len = 0;
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300108 u32 tcp_seq, tcp_ack;
109 int tcp_data_len;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800110 const char *payload1 = "HTTP/1.1 200 OK\r\n"
111 "Content-Length: 30\r\n\r\n\r\n"
112 "<html><body>Hi</body></html>\r\n";
113
114 /* Don't allow the buffer to overrun */
115 if (priv->recv_packets >= PKTBUFSRX)
116 return 0;
117
118 eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
119 memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
120 memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
121 eth_send->et_protlen = htons(PROT_IP);
122 tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
123 tcp_send->tcp_src = tcp->tcp_dst;
124 tcp_send->tcp_dst = tcp->tcp_src;
125 data = (void *)tcp_send + IP_TCP_HDR_SIZE;
126
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300127 tcp_seq = ntohl(tcp->tcp_seq) - priv->irs;
128 tcp_ack = ntohl(tcp->tcp_ack) - priv->iss;
129 tcp_data_len = len - ETHER_HDR_SIZE - IP_HDR_SIZE - GET_TCP_HDR_LEN_IN_BYTES(tcp->tcp_hlen);
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900130
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300131 if (tcp->tcp_flags & TCP_FIN)
132 tcp_data_len++;
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900133
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300134 tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
135 tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + tcp_data_len);
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900136
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300137 if (tcp_seq == 1 && tcp_ack == 1) {
138 if (tcp_data_len == 0) {
139 /* no data, wait for GET request */
140 return -1;
141 }
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900142
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300143 /* reply to GET request */
144 payload_len = strlen(payload1);
145 memcpy(data, payload1, payload_len);
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900146 tcp_send->tcp_flags = TCP_ACK;
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300147 } else if (tcp_ack == 1 + strlen(payload1)) {
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800148 payload_len = 0;
149 tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300150 } else if (tcp_ack == 2 + strlen(payload1)) {
151 payload_len = 0;
152 tcp_send->tcp_flags = TCP_ACK;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800153 }
154
155 tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
156 tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
157 tcp_send->tcp_xsum = 0;
158 tcp_send->tcp_ugr = 0;
159 pkt_len = IP_TCP_HDR_SIZE + payload_len;
160 tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
161 tcp->ip_src,
162 tcp->ip_dst,
163 pkt_len - IP_HDR_SIZE,
164 pkt_len);
165 net_set_ip_header((uchar *)tcp_send,
166 tcp->ip_src,
167 tcp->ip_dst,
168 pkt_len,
169 IPPROTO_TCP);
170
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900171 priv->recv_packet_length[priv->recv_packets] =
172 ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
173 ++priv->recv_packets;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800174
175 return 0;
176}
177
178static int sb_http_handler(struct udevice *dev, void *packet,
179 unsigned int len)
180{
181 struct ethernet_hdr *eth = packet;
182 struct ip_hdr *ip;
183 struct ip_tcp_hdr *tcp;
184
185 if (ntohs(eth->et_protlen) == PROT_ARP) {
186 return sb_arp_handler(dev, packet, len);
187 } else if (ntohs(eth->et_protlen) == PROT_IP) {
188 ip = packet + ETHER_HDR_SIZE;
189 if (ip->ip_p == IPPROTO_TCP) {
190 tcp = packet + ETHER_HDR_SIZE;
191 if (tcp->tcp_flags == TCP_SYN)
192 return sb_syn_handler(dev, packet, len);
193 else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
194 return sb_ack_handler(dev, packet, len);
195 return 0;
196 }
197 return -EPROTONOSUPPORT;
198 }
199
200 return -EPROTONOSUPPORT;
201}
202
203static int net_test_wget(struct unit_test_state *uts)
204{
Jerome Forissier97871922024-11-15 17:45:14 +0100205 char *prev_ethact = env_get("ethact");
206 char *prev_ethrotate = env_get("ethrotate");
207 char *prev_loadaddr = env_get("loadaddr");
208
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800209 sandbox_eth_set_tx_handler(0, sb_http_handler);
210 sandbox_eth_set_priv(0, uts);
211
212 env_set("ethact", "eth@10002000");
213 env_set("ethrotate", "no");
214 env_set("loadaddr", "0x20000");
215 ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0));
Mikhail Kshevetskiye8a0ebc2024-12-28 13:46:33 +0300216 ut_assert_nextline_empty();
Simon Glass56228252024-08-22 07:57:58 -0600217 ut_assert_nextline("Packets received 5, Transfer Successful");
218 ut_assert_nextline("Bytes transferred = 32 (20 hex)");
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800219
220 sandbox_eth_set_tx_handler(0, NULL);
221
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800222 run_command("md5sum ${loadaddr} ${filesize}", 0);
223 ut_assert_nextline("md5 for 00020000 ... 0002001f ==> 234af48e94b0085060249ecb5942ab57");
Simon Glassc579bd42024-08-22 07:58:03 -0600224 ut_assert_console_end();
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800225
Jerome Forissier97871922024-11-15 17:45:14 +0100226 env_set("ethact", prev_ethact);
227 env_set("ethrotate", prev_ethrotate);
228 env_set("loadaddr", prev_loadaddr);
229
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800230 return 0;
231}
Jerome Forissier97871922024-11-15 17:45:14 +0100232CMD_TEST(net_test_wget, UTF_CONSOLE);