1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <efi_selftest.h>
18
19
20
21
22static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
23
24struct dhcp_hdr {
25 u8 op;
26#define BOOTREQUEST 1
27#define BOOTREPLY 2
28 u8 htype;
29# define HWT_ETHER 1
30 u8 hlen;
31# define HWL_ETHER 6
32 u8 hops;
33 u32 xid;
34 u16 secs;
35 u16 flags;
36#define DHCP_FLAGS_UNICAST 0x0000
37#define DHCP_FLAGS_BROADCAST 0x0080
38 u32 ciaddr;
39 u32 yiaddr;
40 u32 siaddr;
41 u32 giaddr;
42 u8 chaddr[16];
43 u8 sname[64];
44 u8 file[128];
45};
46
47
48
49
50#define DHCP_MESSAGE_TYPE 0x35
51#define DHCPDISCOVER 1
52#define DHCPOFFER 2
53#define DHCPREQUEST 3
54#define DHCPDECLINE 4
55#define DHCPACK 5
56#define DHCPNAK 6
57#define DHCPRELEASE 7
58
59struct dhcp {
60 struct ethernet_hdr eth_hdr;
61 struct ip_udp_hdr ip_udp;
62 struct dhcp_hdr dhcp_hdr;
63 u8 opt[128];
64} __packed;
65
66static struct efi_boot_services *boottime;
67static struct efi_simple_network *net;
68static struct efi_event *timer;
69static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
70
71static unsigned int net_ip_id;
72
73
74
75
76
77
78
79
80
81static unsigned int efi_ip_checksum(const void *buf, size_t len)
82{
83 size_t i;
84 u32 sum = 0;
85 const u16 *pos = buf;
86
87 for (i = 0; i < len; i += 2)
88 sum += *pos++;
89
90 sum = (sum >> 16) + (sum & 0xffff);
91 sum += sum >> 16;
92 sum = ~sum & 0xffff;
93
94 return sum;
95}
96
97
98
99
100static efi_status_t send_dhcp_discover(void)
101{
102 efi_status_t ret;
103 struct dhcp p = {};
104
105
106
107
108 boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
109 boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
110 ARP_HLEN);
111 p.eth_hdr.et_protlen = htons(PROT_IP);
112
113
114
115 p.ip_udp.ip_hl_v = 0x45;
116 p.ip_udp.ip_len = htons(sizeof(struct dhcp) -
117 sizeof(struct ethernet_hdr));
118 p.ip_udp.ip_id = htons(++net_ip_id);
119 p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG);
120 p.ip_udp.ip_ttl = 0xff;
121 p.ip_udp.ip_p = IPPROTO_UDP;
122 boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
123 p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
124
125
126
127
128 p.ip_udp.udp_src = htons(68);
129 p.ip_udp.udp_dst = htons(67);
130 p.ip_udp.udp_len = htons(sizeof(struct dhcp) -
131 sizeof(struct ethernet_hdr) -
132 sizeof(struct ip_hdr));
133
134
135
136 p.dhcp_hdr.op = BOOTREQUEST;
137 p.dhcp_hdr.htype = HWT_ETHER;
138 p.dhcp_hdr.hlen = HWL_ETHER;
139 p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST);
140 boottime->copy_mem(&p.dhcp_hdr.chaddr,
141 &net->mode->current_address, ARP_HLEN);
142
143
144
145 p.opt[0] = 0x63;
146 p.opt[1] = 0x82;
147 p.opt[2] = 0x53;
148 p.opt[3] = 0x63;
149 p.opt[4] = DHCP_MESSAGE_TYPE;
150 p.opt[5] = 0x01;
151 p.opt[6] = DHCPDISCOVER;
152 p.opt[7] = 0x39;
153 p.opt[8] = 0x02;
154 p.opt[9] = 0x02;
155 p.opt[10] = 0x40;
156 p.opt[11] = 0xff;
157
158
159
160
161 ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
162 if (ret != EFI_SUCCESS)
163 efi_st_error("Sending a DHCP request failed\n");
164 else
165 efi_st_printf("DHCP Discover\n");
166 return ret;
167}
168
169
170
171
172
173
174
175
176
177
178
179static int setup(const efi_handle_t handle,
180 const struct efi_system_table *systable)
181{
182 efi_status_t ret;
183
184 boottime = systable->boottime;
185
186
187
188
189 ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
190 &timer);
191 if (ret != EFI_SUCCESS) {
192 efi_st_error("Failed to create event\n");
193 return EFI_ST_FAILURE;
194 }
195
196
197
198 ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
199 if (ret != EFI_SUCCESS) {
200 efi_st_error("Failed to set timer\n");
201 return EFI_ST_FAILURE;
202 }
203
204
205
206 ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
207 if (ret != EFI_SUCCESS) {
208 net = NULL;
209 efi_st_error("Failed to locate simple network protocol\n");
210 return EFI_ST_FAILURE;
211 }
212
213
214
215 if (!net->mode) {
216 efi_st_error("Mode not provided\n");
217 return EFI_ST_FAILURE;
218 }
219 if (net->mode->hwaddr_size != ARP_HLEN) {
220 efi_st_error("HwAddressSize = %u, expected %u\n",
221 net->mode->hwaddr_size, ARP_HLEN);
222 return EFI_ST_FAILURE;
223 }
224
225
226
227 if (!net->wait_for_packet) {
228 efi_st_error("WaitForPacket event missing\n");
229 return EFI_ST_FAILURE;
230 }
231
232
233
234 ret = net->initialize(net, 0, 0);
235 if (ret != EFI_SUCCESS) {
236 efi_st_error("Failed to initialize network adapter\n");
237 return EFI_ST_FAILURE;
238 }
239
240
241
242 ret = net->start(net);
243 if (ret != EFI_SUCCESS) {
244 efi_st_error("Failed to start network adapter\n");
245 return EFI_ST_FAILURE;
246 }
247 return EFI_ST_SUCCESS;
248}
249
250
251
252
253
254
255
256
257
258static int execute(void)
259{
260 efi_status_t ret;
261 struct efi_event *events[2];
262 efi_uintn_t index;
263 union {
264 struct dhcp p;
265 u8 b[PKTSIZE];
266 } buffer;
267 struct efi_mac_address srcaddr;
268 struct efi_mac_address destaddr;
269 size_t buffer_size;
270 u8 *addr;
271
272
273
274 unsigned int timeout = 10;
275
276
277 if (!net || !timer) {
278 efi_st_error("Cannot execute test after setup failure\n");
279 return EFI_ST_FAILURE;
280 }
281
282
283
284
285 ret = send_dhcp_discover();
286 if (ret != EFI_SUCCESS)
287 return EFI_ST_FAILURE;
288
289
290
291
292
293
294
295
296
297
298 events[0] = timer;
299 events[1] = net->wait_for_packet;
300 for (;;) {
301
302
303
304 boottime->wait_for_event(2, events, &index);
305 if (index == 0) {
306
307
308
309 --timeout;
310 if (!timeout) {
311 efi_st_error("Timeout occurred\n");
312 return EFI_ST_FAILURE;
313 }
314
315
316
317 ret = send_dhcp_discover();
318 if (ret != EFI_SUCCESS)
319 return EFI_ST_FAILURE;
320 continue;
321 }
322
323
324
325 buffer_size = sizeof(buffer);
326 net->receive(net, NULL, &buffer_size, &buffer,
327 &srcaddr, &destaddr, NULL);
328 if (ret != EFI_SUCCESS) {
329 efi_st_error("Failed to receive packet");
330 return EFI_ST_FAILURE;
331 }
332
333
334
335
336
337 if (efi_st_memcmp(&destaddr, &net->mode->current_address,
338 ARP_HLEN) &&
339 efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
340 continue;
341
342
343
344 if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
345 buffer.p.ip_udp.ip_hl_v != 0x45 ||
346 buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
347 buffer.p.ip_udp.udp_src != ntohs(67) ||
348 buffer.p.ip_udp.udp_dst != ntohs(68) ||
349 buffer.p.dhcp_hdr.op != BOOTREPLY)
350 continue;
351
352
353
354 break;
355 }
356
357
358
359
360 addr = (u8 *)&buffer.p.ip_udp.ip_src;
361 efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
362 addr[0], addr[1], addr[2], addr[3], &srcaddr);
363 if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
364 efi_st_printf("as broadcast message.\n");
365 else
366 efi_st_printf("as unicast message.\n");
367
368 return EFI_ST_SUCCESS;
369}
370
371
372
373
374
375
376
377
378
379static int teardown(void)
380{
381 efi_status_t ret;
382 int exit_status = EFI_ST_SUCCESS;
383
384 if (timer) {
385
386
387
388 ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
389 if (ret != EFI_SUCCESS) {
390 efi_st_error("Failed to stop timer");
391 exit_status = EFI_ST_FAILURE;
392 }
393
394
395
396 ret = boottime->close_event(timer);
397 if (ret != EFI_SUCCESS) {
398 efi_st_error("Failed to close event");
399 exit_status = EFI_ST_FAILURE;
400 }
401 }
402 if (net) {
403
404
405
406 ret = net->stop(net);
407 if (ret != EFI_SUCCESS) {
408 efi_st_error("Failed to stop network adapter\n");
409 exit_status = EFI_ST_FAILURE;
410 }
411
412
413
414 ret = net->shutdown(net);
415 if (ret != EFI_SUCCESS) {
416 efi_st_error("Failed to shut down network adapter\n");
417 exit_status = EFI_ST_FAILURE;
418 }
419 }
420
421 return exit_status;
422}
423
424EFI_UNIT_TEST(snp) = {
425 .name = "simple network protocol",
426 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
427 .setup = setup,
428 .execute = execute,
429 .teardown = teardown,
430};
431