1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_snp
4  *
5  * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This unit test covers the Simple Network Protocol as well as
8  * the CopyMem and SetMem boottime services.
9  *
10  * A DHCP discover message is sent. The test is successful if a
11  * DHCP reply is received.
12  *
13  * TODO: Once ConnectController and DisconnectController are implemented
14  *	 we should connect our code as controller.
15  */
16 
17 #include <efi_selftest.h>
18 #include <net.h>
19 
20 /*
21  * MAC address for broadcasts
22  */
23 static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
24 
25 struct dhcp_hdr {
26 	u8 op;
27 #define BOOTREQUEST 1
28 #define BOOTREPLY 2
29 	u8 htype;
30 # define HWT_ETHER 1
31 	u8 hlen;
32 # define HWL_ETHER 6
33 	u8 hops;
34 	u32 xid;
35 	u16 secs;
36 	u16 flags;
37 #define DHCP_FLAGS_UNICAST	0x0000
38 #define DHCP_FLAGS_BROADCAST	0x0080
39 	u32 ciaddr;
40 	u32 yiaddr;
41 	u32 siaddr;
42 	u32 giaddr;
43 	u8 chaddr[16];
44 	u8 sname[64];
45 	u8 file[128];
46 };
47 
48 /*
49  * Message type option.
50  */
51 #define DHCP_MESSAGE_TYPE	0x35
52 #define DHCPDISCOVER		1
53 #define DHCPOFFER		2
54 #define DHCPREQUEST		3
55 #define DHCPDECLINE		4
56 #define DHCPACK			5
57 #define DHCPNAK			6
58 #define DHCPRELEASE		7
59 
60 struct dhcp {
61 	struct ethernet_hdr eth_hdr;
62 	struct ip_udp_hdr ip_udp;
63 	struct dhcp_hdr dhcp_hdr;
64 	u8 opt[128];
65 } __packed;
66 
67 static struct efi_boot_services *boottime;
68 static struct efi_simple_network *net;
69 static struct efi_event *timer;
70 /* IP packet ID */
71 static unsigned int net_ip_id;
72 
73 /*
74  * Compute the checksum of the IP header. We cover even values of length only.
75  * We cannot use net/checksum.c due to different CFLAGS values.
76  *
77  * @buf:	IP header
78  * @len:	length of header in bytes
79  * Return:	checksum
80  */
efi_ip_checksum(const void * buf,size_t len)81 static 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  * Transmit a DHCPDISCOVER message.
99  */
send_dhcp_discover(void)100 static efi_status_t send_dhcp_discover(void)
101 {
102 	efi_status_t ret;
103 	struct dhcp p = {};
104 
105 	/*
106 	 * Fill Ethernet header
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 	 * Fill IP header
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; /* time to live */
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 	 * Fill UDP header
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 	 * Fill DHCP header
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 	 * Fill options
144 	 */
145 	p.opt[0]	= 0x63; /* DHCP magic cookie */
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; /* length */
151 	p.opt[6]	= DHCPDISCOVER;
152 	p.opt[7]	= 0x39; /* maximum message size */
153 	p.opt[8]	= 0x02; /* length */
154 	p.opt[9]	= 0x02; /* 576 bytes */
155 	p.opt[10]	= 0x40;
156 	p.opt[11]	= 0xff; /* end of options */
157 
158 	/*
159 	 * Transmit DHCPDISCOVER message.
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  * Setup unit test.
171  *
172  * Create a 1 s periodic timer.
173  * Start the network driver.
174  *
175  * @handle:	handle of the loaded image
176  * @systable:	system table
177  * Return:	EFI_ST_SUCCESS for success
178  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)179 static 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 	 * Create a timer event.
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 	 * Set timer period to 1s.
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 	 * Find an interface implementing the SNP protocol.
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 	 * Check hardware address size.
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 	 * Check that WaitForPacket event exists.
226 	 */
227 	if (!net->wait_for_packet) {
228 		efi_st_error("WaitForPacket event missing\n");
229 		return EFI_ST_FAILURE;
230 	}
231 	if (net->mode->state == EFI_NETWORK_INITIALIZED) {
232 		/*
233 		 * Shut down network adapter.
234 		 */
235 		ret = net->shutdown(net);
236 		if (ret != EFI_SUCCESS) {
237 			efi_st_error("Failed to shut down network adapter\n");
238 			return EFI_ST_FAILURE;
239 		}
240 	}
241 	if (net->mode->state == EFI_NETWORK_STARTED) {
242 		/*
243 		 * Stop network adapter.
244 		 */
245 		ret = net->stop(net);
246 		if (ret != EFI_SUCCESS) {
247 			efi_st_error("Failed to stop network adapter\n");
248 			return EFI_ST_FAILURE;
249 		}
250 	}
251 	/*
252 	 * Start network adapter.
253 	 */
254 	ret = net->start(net);
255 	if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
256 		efi_st_error("Failed to start network adapter\n");
257 		return EFI_ST_FAILURE;
258 	}
259 	if (net->mode->state != EFI_NETWORK_STARTED) {
260 		efi_st_error("Failed to start network adapter\n");
261 		return EFI_ST_FAILURE;
262 	}
263 	/*
264 	 * Initialize network adapter.
265 	 */
266 	ret = net->initialize(net, 0, 0);
267 	if (ret != EFI_SUCCESS) {
268 		efi_st_error("Failed to initialize network adapter\n");
269 		return EFI_ST_FAILURE;
270 	}
271 	if (net->mode->state != EFI_NETWORK_INITIALIZED) {
272 		efi_st_error("Failed to initialize network adapter\n");
273 		return EFI_ST_FAILURE;
274 	}
275 	return EFI_ST_SUCCESS;
276 }
277 
278 /*
279  * Execute unit test.
280  *
281  * A DHCP discover message is sent. The test is successful if a
282  * DHCP reply is received within 10 seconds.
283  *
284  * Return:	EFI_ST_SUCCESS for success
285  */
execute(void)286 static int execute(void)
287 {
288 	efi_status_t ret;
289 	struct efi_event *events[2];
290 	efi_uintn_t index;
291 	union {
292 		struct dhcp p;
293 		u8 b[PKTSIZE];
294 	} buffer;
295 	struct efi_mac_address srcaddr;
296 	struct efi_mac_address destaddr;
297 	size_t buffer_size;
298 	u8 *addr;
299 
300 	/*
301 	 * The timeout is to occur after 10 s.
302 	 */
303 	unsigned int timeout = 10;
304 
305 	/* Setup may have failed */
306 	if (!net || !timer) {
307 		efi_st_error("Cannot execute test after setup failure\n");
308 		return EFI_ST_FAILURE;
309 	}
310 
311 	/* Check media connected */
312 	ret = net->get_status(net, NULL, NULL);
313 	if (ret != EFI_SUCCESS) {
314 		efi_st_error("Failed to get status");
315 		return EFI_ST_FAILURE;
316 	}
317 	if (net->mode && net->mode->media_present_supported &&
318 	    !net->mode->media_present) {
319 		efi_st_error("Network media is not connected");
320 		return EFI_ST_FAILURE;
321 	}
322 
323 	/*
324 	 * Send DHCP discover message
325 	 */
326 	ret = send_dhcp_discover();
327 	if (ret != EFI_SUCCESS)
328 		return EFI_ST_FAILURE;
329 
330 	/*
331 	 * If we would call WaitForEvent only with the WaitForPacket event,
332 	 * our code would block until a packet is received which might never
333 	 * occur. By calling WaitFor event with both a timer event and the
334 	 * WaitForPacket event we can escape this blocking situation.
335 	 *
336 	 * If the timer event occurs before we have received a DHCP reply
337 	 * a further DHCP discover message is sent.
338 	 */
339 	events[0] = timer;
340 	events[1] = net->wait_for_packet;
341 	for (;;) {
342 		/*
343 		 * Wait for packet to be received or timer event.
344 		 */
345 		boottime->wait_for_event(2, events, &index);
346 		if (index == 0) {
347 			/*
348 			 * The timer event occurred. Check for timeout.
349 			 */
350 			--timeout;
351 			if (!timeout) {
352 				efi_st_error("Timeout occurred\n");
353 				return EFI_ST_FAILURE;
354 			}
355 			/*
356 			 * Send further DHCP discover message
357 			 */
358 			ret = send_dhcp_discover();
359 			if (ret != EFI_SUCCESS)
360 				return EFI_ST_FAILURE;
361 			continue;
362 		}
363 		/*
364 		 * Receive packets until buffer is empty
365 		 */
366 		for (;;) {
367 			buffer_size = sizeof(buffer);
368 			ret = net->receive(net, NULL, &buffer_size, &buffer,
369 					   &srcaddr, &destaddr, NULL);
370 			if (ret == EFI_NOT_READY) {
371 				/* The received buffer is empty. */
372 				break;
373 			}
374 
375 			if (ret != EFI_SUCCESS) {
376 				efi_st_error("Failed to receive packet");
377 				return EFI_ST_FAILURE;
378 			}
379 			/*
380 			 * Check the packet is meant for this system.
381 			 * Unfortunately QEMU ignores the broadcast flag.
382 			 * So we have to check for broadcasts too.
383 			 */
384 			if (memcmp(&destaddr, &net->mode->current_address, ARP_HLEN) &&
385 			    memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
386 				continue;
387 			/*
388 			 * Check this is a DHCP reply
389 			 */
390 			if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
391 			    buffer.p.ip_udp.ip_hl_v != 0x45 ||
392 			    buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
393 			    buffer.p.ip_udp.udp_src != ntohs(67) ||
394 			    buffer.p.ip_udp.udp_dst != ntohs(68) ||
395 			    buffer.p.dhcp_hdr.op != BOOTREPLY)
396 				continue;
397 			/*
398 			 * We successfully received a DHCP reply.
399 			 */
400 			goto received;
401 		}
402 	}
403 received:
404 	/*
405 	 * Write a log message.
406 	 */
407 	addr = (u8 *)&buffer.p.ip_udp.ip_src;
408 	efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
409 		      addr[0], addr[1], addr[2], addr[3], &srcaddr);
410 	if (!memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
411 		efi_st_printf("as broadcast message.\n");
412 	else
413 		efi_st_printf("as unicast message.\n");
414 
415 	return EFI_ST_SUCCESS;
416 }
417 
418 /*
419  * Tear down unit test.
420  *
421  * Close the timer event created in setup.
422  * Shut down the network adapter.
423  *
424  * Return:	EFI_ST_SUCCESS for success
425  */
teardown(void)426 static int teardown(void)
427 {
428 	efi_status_t ret;
429 	int exit_status = EFI_ST_SUCCESS;
430 
431 	if (timer) {
432 		/*
433 		 * Stop timer.
434 		 */
435 		ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
436 		if (ret != EFI_SUCCESS) {
437 			efi_st_error("Failed to stop timer");
438 			exit_status = EFI_ST_FAILURE;
439 		}
440 		/*
441 		 * Close timer event.
442 		 */
443 		ret = boottime->close_event(timer);
444 		if (ret != EFI_SUCCESS) {
445 			efi_st_error("Failed to close event");
446 			exit_status = EFI_ST_FAILURE;
447 		}
448 	}
449 	if (net) {
450 		/*
451 		 * Shut down network adapter.
452 		 */
453 		ret = net->shutdown(net);
454 		if (ret != EFI_SUCCESS) {
455 			efi_st_error("Failed to shut down network adapter\n");
456 			exit_status = EFI_ST_FAILURE;
457 		}
458 		if (net->mode->state != EFI_NETWORK_STARTED) {
459 			efi_st_error("Failed to shutdown network adapter\n");
460 			return EFI_ST_FAILURE;
461 		}
462 		/*
463 		 * Stop network adapter.
464 		 */
465 		ret = net->stop(net);
466 		if (ret != EFI_SUCCESS) {
467 			efi_st_error("Failed to stop network adapter\n");
468 			exit_status = EFI_ST_FAILURE;
469 		}
470 		if (net->mode->state != EFI_NETWORK_STOPPED) {
471 			efi_st_error("Failed to stop network adapter\n");
472 			return EFI_ST_FAILURE;
473 		}
474 	}
475 
476 	return exit_status;
477 }
478 
479 EFI_UNIT_TEST(snp) = {
480 	.name = "simple network protocol",
481 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
482 	.setup = setup,
483 	.execute = execute,
484 	.teardown = teardown,
485 #ifdef CONFIG_SANDBOX
486 	/*
487 	 * Running this test on the sandbox requires setting environment
488 	 * variable ethact to a network interface connected to a DHCP server and
489 	 * ethrotate to 'no'.
490 	 */
491 	.on_request = true,
492 #endif
493 };
494