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