1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
4  */
5 
6 #include <dm.h>
7 #include <spl.h>
8 #include <test/spl.h>
9 #include <asm/eth.h>
10 #include <test/ut.h>
11 #include "../../net/bootp.h"
12 
13 /*
14  * sandbox_eth_bootp_req_to_reply()
15  *
16  * Check if a BOOTP request was sent. If so, inject a reply
17  *
18  * returns 0 if injected, -EAGAIN if not
19  */
sandbox_eth_bootp_req_to_reply(struct udevice * dev,void * packet,unsigned int len)20 static int sandbox_eth_bootp_req_to_reply(struct udevice *dev, void *packet,
21 					  unsigned int len)
22 {
23 	struct eth_sandbox_priv *priv = dev_get_priv(dev);
24 	struct ethernet_hdr *eth = packet;
25 	struct ip_udp_hdr *ip;
26 	struct bootp_hdr *bp;
27 	struct ethernet_hdr *eth_recv;
28 	struct ip_udp_hdr *ipr;
29 	struct bootp_hdr *bpr;
30 
31 	if (ntohs(eth->et_protlen) != PROT_IP)
32 		return -EAGAIN;
33 
34 	ip = packet + ETHER_HDR_SIZE;
35 	if (ip->ip_p != IPPROTO_UDP)
36 		return -EAGAIN;
37 
38 	if (ntohs(ip->udp_dst) != PORT_BOOTPS)
39 		return -EAGAIN;
40 
41 	bp = (void *)ip + IP_UDP_HDR_SIZE;
42 	if (bp->bp_op != OP_BOOTREQUEST)
43 		return -EAGAIN;
44 
45 	/* Don't allow the buffer to overrun */
46 	if (priv->recv_packets >= PKTBUFSRX)
47 		return 0;
48 
49 	/* reply to the request */
50 	eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
51 	memcpy(eth_recv, packet, len);
52 	ipr = (void *)eth_recv + ETHER_HDR_SIZE;
53 	bpr = (void *)ipr + IP_UDP_HDR_SIZE;
54 	memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
55 	memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
56 	ipr->ip_sum = 0;
57 	ipr->ip_off = 0;
58 	net_write_ip(&ipr->ip_dst, net_ip);
59 	net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr);
60 	ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
61 	ipr->udp_src = ip->udp_dst;
62 	ipr->udp_dst = ip->udp_src;
63 
64 	bpr->bp_op = OP_BOOTREPLY;
65 	net_write_ip(&bpr->bp_yiaddr, net_ip);
66 	net_write_ip(&bpr->bp_siaddr, priv->fake_host_ipaddr);
67 	copy_filename(bpr->bp_file, CONFIG_BOOTFILE, sizeof(CONFIG_BOOTFILE));
68 	memset(&bpr->bp_vend, 0, sizeof(bpr->bp_vend));
69 
70 	priv->recv_packet_length[priv->recv_packets] = len;
71 	++priv->recv_packets;
72 
73 	return 0;
74 }
75 
76 struct spl_test_net_priv {
77 	struct unit_test_state *uts;
78 	void *img;
79 	size_t img_size;
80 	u16 port;
81 };
82 
83 /* Well known TFTP port # */
84 #define TFTP_PORT	69
85 /* Transaction ID, chosen at random */
86 #define	TFTP_TID	21313
87 
88 /*
89  *	TFTP operations.
90  */
91 #define TFTP_RRQ	1
92 #define TFTP_DATA	3
93 #define TFTP_ACK	4
94 
95 /* default TFTP block size */
96 #define TFTP_BLOCK_SIZE		512
97 
98 struct tftp_hdr {
99 	u16 opcode;
100 	u16 block;
101 };
102 
103 #define TFTP_HDR_SIZE sizeof(struct tftp_hdr)
104 
105 /*
106  * sandbox_eth_tftp_req_to_reply()
107  *
108  * Check if a TFTP request was sent. If so, inject a reply. We don't support
109  * options, and we don't check for rollover, so we are limited files of less
110  * than 32M.
111  *
112  * returns 0 if injected, -EAGAIN if not
113  */
sandbox_eth_tftp_req_to_reply(struct udevice * dev,void * packet,unsigned int len)114 static int sandbox_eth_tftp_req_to_reply(struct udevice *dev, void *packet,
115 					 unsigned int len)
116 {
117 	struct eth_sandbox_priv *priv = dev_get_priv(dev);
118 	struct spl_test_net_priv *test_priv = priv->priv;
119 	struct ethernet_hdr *eth = packet;
120 	struct ip_udp_hdr *ip;
121 	struct tftp_hdr *tftp;
122 	struct ethernet_hdr *eth_recv;
123 	struct ip_udp_hdr *ipr;
124 	struct tftp_hdr *tftpr;
125 	size_t size;
126 	u16 block;
127 
128 	if (ntohs(eth->et_protlen) != PROT_IP)
129 		return -EAGAIN;
130 
131 	ip = packet + ETHER_HDR_SIZE;
132 	if (ip->ip_p != IPPROTO_UDP)
133 		return -EAGAIN;
134 
135 	if (ntohs(ip->udp_dst) == TFTP_PORT) {
136 		tftp = (void *)ip + IP_UDP_HDR_SIZE;
137 		if (htons(tftp->opcode) != TFTP_RRQ)
138 			return -EAGAIN;
139 
140 		block = 0;
141 	} else if (ntohs(ip->udp_dst) == TFTP_TID) {
142 		tftp = (void *)ip + IP_UDP_HDR_SIZE;
143 		if (htons(tftp->opcode) != TFTP_ACK)
144 			return -EAGAIN;
145 
146 		block = htons(tftp->block);
147 	} else {
148 		return -EAGAIN;
149 	}
150 
151 	if (block * TFTP_BLOCK_SIZE > test_priv->img_size)
152 		return 0;
153 
154 	size = min(test_priv->img_size - block * TFTP_BLOCK_SIZE,
155 		   (size_t)TFTP_BLOCK_SIZE);
156 
157 	/* Don't allow the buffer to overrun */
158 	if (priv->recv_packets >= PKTBUFSRX)
159 		return 0;
160 
161 	/* reply to the request */
162 	eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
163 	memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
164 	memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
165 	eth_recv->et_protlen = htons(PROT_IP);
166 
167 	ipr = (void *)eth_recv + ETHER_HDR_SIZE;
168 	ipr->ip_hl_v = 0x45;
169 	ipr->ip_len = htons(IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
170 	ipr->ip_off = htons(IP_FLAGS_DFRAG);
171 	ipr->ip_ttl = 255;
172 	ipr->ip_p = IPPROTO_UDP;
173 	ipr->ip_sum = 0;
174 	net_copy_ip(&ipr->ip_dst, &ip->ip_src);
175 	net_copy_ip(&ipr->ip_src, &ip->ip_dst);
176 	ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
177 
178 	ipr->udp_src = htons(TFTP_TID);
179 	ipr->udp_dst = ip->udp_src;
180 	ipr->udp_len = htons(UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
181 	ipr->udp_xsum = 0;
182 
183 	tftpr = (void *)ipr + IP_UDP_HDR_SIZE;
184 	tftpr->opcode = htons(TFTP_DATA);
185 	tftpr->block = htons(block + 1);
186 	memcpy((void *)tftpr + TFTP_HDR_SIZE,
187 	       test_priv->img + block * TFTP_BLOCK_SIZE, size);
188 
189 	priv->recv_packet_length[priv->recv_packets] =
190 		ETHER_HDR_SIZE + IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size;
191 	++priv->recv_packets;
192 
193 	return 0;
194 }
195 
spl_net_handler(struct udevice * dev,void * packet,unsigned int len)196 static int spl_net_handler(struct udevice *dev, void *packet,
197 			   unsigned int len)
198 {
199 	struct eth_sandbox_priv *priv = dev_get_priv(dev);
200 	int old_packets = priv->recv_packets;
201 
202 	priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
203 	net_ip = string_to_ip("1.1.2.2");
204 
205 	sandbox_eth_arp_req_to_reply(dev, packet, len);
206 	sandbox_eth_bootp_req_to_reply(dev, packet, len);
207 	sandbox_eth_tftp_req_to_reply(dev, packet, len);
208 
209 	if (old_packets == priv->recv_packets)
210 		return 0;
211 
212 	return 0;
213 }
214 
spl_test_net_write_image(struct unit_test_state * uts,void * img,size_t img_size)215 static int spl_test_net_write_image(struct unit_test_state *uts, void *img,
216 				    size_t img_size)
217 {
218 	struct spl_test_net_priv *test_priv = malloc(sizeof(*test_priv));
219 
220 	ut_assertnonnull(test_priv);
221 	test_priv->uts = uts;
222 	test_priv->img = img;
223 	test_priv->img_size = img_size;
224 
225 	sandbox_eth_set_tx_handler(0, spl_net_handler);
226 	sandbox_eth_set_priv(0, test_priv);
227 	return 0;
228 }
229 
spl_test_net(struct unit_test_state * uts,const char * test_name,enum spl_test_image type)230 static int spl_test_net(struct unit_test_state *uts, const char *test_name,
231 			enum spl_test_image type)
232 {
233 	struct eth_sandbox_priv *priv;
234 	struct udevice *dev;
235 	int ret;
236 
237 	net_server_ip = string_to_ip("1.1.2.4");
238 	ret = do_spl_test_load(uts, test_name, type,
239 			       SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_CPGMAC,
240 						  spl_net_load_image_cpgmac),
241 			       spl_test_net_write_image);
242 
243 	sandbox_eth_set_tx_handler(0, NULL);
244 	ut_assertok(uclass_get_device(UCLASS_ETH, 0, &dev));
245 	priv = dev_get_priv(dev);
246 	free(priv->priv);
247 	return ret;
248 }
249 SPL_IMG_TEST(spl_test_net, LEGACY, DM_FLAGS);
250 SPL_IMG_TEST(spl_test_net, LEGACY_LZMA, DM_FLAGS);
251 SPL_IMG_TEST(spl_test_net, IMX8, DM_FLAGS);
252 SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
253 SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);
254