blob: fe26fee54c9d83b355ff94c62e3e54bcde177aee [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>
22#include <test/lib.h>
23#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)
28
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +090029int net_set_ack_options(union tcp_build_pkt *b);
30
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +080031static int sb_arp_handler(struct udevice *dev, void *packet,
32 unsigned int len)
33{
34 struct eth_sandbox_priv *priv = dev_get_priv(dev);
35 struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
36 int ret = 0;
37
38 if (ntohs(arp->ar_op) == ARPOP_REQUEST) {
39 priv->fake_host_ipaddr = net_read_ip(&arp->ar_spa);
40
41 ret = sandbox_eth_recv_arp_req(dev);
42 if (ret)
43 return ret;
44 ret = sandbox_eth_arp_req_to_reply(dev, packet, len);
45 return ret;
46 }
47
48 return -EPROTONOSUPPORT;
49}
50
51static int sb_syn_handler(struct udevice *dev, void *packet,
52 unsigned int len)
53{
54 struct eth_sandbox_priv *priv = dev_get_priv(dev);
55 struct ethernet_hdr *eth = packet;
56 struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
57 struct ethernet_hdr *eth_send;
58 struct ip_tcp_hdr *tcp_send;
59
60 /* Don't allow the buffer to overrun */
61 if (priv->recv_packets >= PKTBUFSRX)
62 return 0;
63
64 eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
65 memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
66 memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
67 eth_send->et_protlen = htons(PROT_IP);
68 tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
69 tcp_send->tcp_src = tcp->tcp_dst;
70 tcp_send->tcp_dst = tcp->tcp_src;
71 tcp_send->tcp_seq = htonl(0);
72 tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
73 tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
74 tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
75 tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
76 tcp_send->tcp_xsum = 0;
77 tcp_send->tcp_ugr = 0;
78 tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
79 tcp->ip_src,
80 tcp->ip_dst,
81 TCP_HDR_SIZE,
82 IP_TCP_HDR_SIZE);
83 net_set_ip_header((uchar *)tcp_send,
84 tcp->ip_src,
85 tcp->ip_dst,
86 IP_TCP_HDR_SIZE,
87 IPPROTO_TCP);
88
89 priv->recv_packet_length[priv->recv_packets] =
90 ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
91 ++priv->recv_packets;
92
93 return 0;
94}
95
96static int sb_ack_handler(struct udevice *dev, void *packet,
97 unsigned int len)
98{
99 struct eth_sandbox_priv *priv = dev_get_priv(dev);
100 struct ethernet_hdr *eth = packet;
101 struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
102 struct ethernet_hdr *eth_send;
103 struct ip_tcp_hdr *tcp_send;
104 void *data;
105 int pkt_len;
106 int payload_len = 0;
107 const char *payload1 = "HTTP/1.1 200 OK\r\n"
108 "Content-Length: 30\r\n\r\n\r\n"
109 "<html><body>Hi</body></html>\r\n";
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900110 union tcp_build_pkt *b = (union tcp_build_pkt *)tcp;
111 const int recv_payload_len = len - net_set_ack_options(b) - IP_HDR_SIZE - ETHER_HDR_SIZE;
112 static int next_seq;
113 const int bottom_payload_len = 10;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800114
115 /* Don't allow the buffer to overrun */
116 if (priv->recv_packets >= PKTBUFSRX)
117 return 0;
118
119 eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
120 memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
121 memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
122 eth_send->et_protlen = htons(PROT_IP);
123 tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
124 tcp_send->tcp_src = tcp->tcp_dst;
125 tcp_send->tcp_dst = tcp->tcp_src;
126 data = (void *)tcp_send + IP_TCP_HDR_SIZE;
127
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900128 if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1 && recv_payload_len == 0) {
129 // ignore ACK for three-way handshaking
130 return 0;
131 } else if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) {
132 // recv HTTP request message and reply top half data
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800133 tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900134 tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + recv_payload_len);
135
136 payload_len = strlen(payload1) - bottom_payload_len;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800137 memcpy(data, payload1, payload_len);
138 tcp_send->tcp_flags = TCP_ACK;
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900139
140 next_seq = ntohl(tcp_send->tcp_seq) + payload_len;
141 } else if (ntohl(tcp->tcp_ack) == next_seq) {
142 // reply bottom half data
143 const int top_payload_len = strlen(payload1) - bottom_payload_len;
144
145 tcp_send->tcp_seq = htonl(next_seq);
146 tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + recv_payload_len);
147
148 payload_len = bottom_payload_len;
149 memcpy(data, payload1 + top_payload_len, payload_len);
150 tcp_send->tcp_flags = TCP_ACK;
151 } else {
152 // close connection
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800153 tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
154 tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
155 payload_len = 0;
156 tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
157 }
158
159 tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
160 tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
161 tcp_send->tcp_xsum = 0;
162 tcp_send->tcp_ugr = 0;
163 pkt_len = IP_TCP_HDR_SIZE + payload_len;
164 tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
165 tcp->ip_src,
166 tcp->ip_dst,
167 pkt_len - IP_HDR_SIZE,
168 pkt_len);
169 net_set_ip_header((uchar *)tcp_send,
170 tcp->ip_src,
171 tcp->ip_dst,
172 pkt_len,
173 IPPROTO_TCP);
174
Yasuharu Shibata0c4813d2024-08-14 21:41:07 +0900175 priv->recv_packet_length[priv->recv_packets] =
176 ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
177 ++priv->recv_packets;
Ying-Chun Liu (PaulLiu)d623b472022-11-08 14:17:31 +0800178
179 return 0;
180}
181
182static int sb_http_handler(struct udevice *dev, void *packet,
183 unsigned int len)
184{
185 struct ethernet_hdr *eth = packet;
186 struct ip_hdr *ip;
187 struct ip_tcp_hdr *tcp;
188
189 if (ntohs(eth->et_protlen) == PROT_ARP) {
190 return sb_arp_handler(dev, packet, len);
191 } else if (ntohs(eth->et_protlen) == PROT_IP) {
192 ip = packet + ETHER_HDR_SIZE;
193 if (ip->ip_p == IPPROTO_TCP) {
194 tcp = packet + ETHER_HDR_SIZE;
195 if (tcp->tcp_flags == TCP_SYN)
196 return sb_syn_handler(dev, packet, len);
197 else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
198 return sb_ack_handler(dev, packet, len);
199 return 0;
200 }
201 return -EPROTONOSUPPORT;
202 }
203
204 return -EPROTONOSUPPORT;
205}
206
207static int net_test_wget(struct unit_test_state *uts)
208{
209 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));
Simon Glass56228252024-08-22 07:57:58 -0600216 ut_assert_nextline("HTTP/1.1 200 OK");
217 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
226 return 0;
227}
Simon Glassaeee87e2024-08-22 07:57:57 -0600228LIB_TEST(net_test_wget, UTF_CONSOLE);