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 static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
71 /* IP packet ID */
72 static unsigned int net_ip_id;
73 
74 /*
75  * Compute the checksum of the IP header. We cover even values of length only.
76  * We cannot use net/checksum.c due to different CFLAGS values.
77  *
78  * @buf:	IP header
79  * @len:	length of header in bytes
80  * @return:	checksum
81  */
efi_ip_checksum(const void * buf,size_t len)82 static unsigned int efi_ip_checksum(const void *buf, size_t len)
83 {
84 	size_t i;
85 	u32 sum = 0;
86 	const u16 *pos = buf;
87 
88 	for (i = 0; i < len; i += 2)
89 		sum += *pos++;
90 
91 	sum = (sum >> 16) + (sum & 0xffff);
92 	sum += sum >> 16;
93 	sum = ~sum & 0xffff;
94 
95 	return sum;
96 }
97 
98 /*
99  * Transmit a DHCPDISCOVER message.
100  */
send_dhcp_discover(void)101 static efi_status_t send_dhcp_discover(void)
102 {
103 	efi_status_t ret;
104 	struct dhcp p = {};
105 
106 	/*
107 	 * Fill Ethernet header
108 	 */
109 	boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
110 	boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
111 			   ARP_HLEN);
112 	p.eth_hdr.et_protlen = htons(PROT_IP);
113 	/*
114 	 * Fill IP header
115 	 */
116 	p.ip_udp.ip_hl_v	= 0x45;
117 	p.ip_udp.ip_len		= htons(sizeof(struct dhcp) -
118 					sizeof(struct ethernet_hdr));
119 	p.ip_udp.ip_id		= htons(++net_ip_id);
120 	p.ip_udp.ip_off		= htons(IP_FLAGS_DFRAG);
121 	p.ip_udp.ip_ttl		= 0xff; /* time to live */
122 	p.ip_udp.ip_p		= IPPROTO_UDP;
123 	boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
124 	p.ip_udp.ip_sum		= efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
125 
126 	/*
127 	 * Fill UDP header
128 	 */
129 	p.ip_udp.udp_src	= htons(68);
130 	p.ip_udp.udp_dst	= htons(67);
131 	p.ip_udp.udp_len	= htons(sizeof(struct dhcp) -
132 					sizeof(struct ethernet_hdr) -
133 					sizeof(struct ip_hdr));
134 	/*
135 	 * Fill DHCP header
136 	 */
137 	p.dhcp_hdr.op		= BOOTREQUEST;
138 	p.dhcp_hdr.htype	= HWT_ETHER;
139 	p.dhcp_hdr.hlen		= HWL_ETHER;
140 	p.dhcp_hdr.flags	= htons(DHCP_FLAGS_UNICAST);
141 	boottime->copy_mem(&p.dhcp_hdr.chaddr,
142 			   &net->mode->current_address, ARP_HLEN);
143 	/*
144 	 * Fill options
145 	 */
146 	p.opt[0]	= 0x63; /* DHCP magic cookie */
147 	p.opt[1]	= 0x82;
148 	p.opt[2]	= 0x53;
149 	p.opt[3]	= 0x63;
150 	p.opt[4]	= DHCP_MESSAGE_TYPE;
151 	p.opt[5]	= 0x01; /* length */
152 	p.opt[6]	= DHCPDISCOVER;
153 	p.opt[7]	= 0x39; /* maximum message size */
154 	p.opt[8]	= 0x02; /* length */
155 	p.opt[9]	= 0x02; /* 576 bytes */
156 	p.opt[10]	= 0x40;
157 	p.opt[11]	= 0xff; /* end of options */
158 
159 	/*
160 	 * Transmit DHCPDISCOVER message.
161 	 */
162 	ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
163 	if (ret != EFI_SUCCESS)
164 		efi_st_error("Sending a DHCP request failed\n");
165 	else
166 		efi_st_printf("DHCP Discover\n");
167 	return ret;
168 }
169 
170 /*
171  * Setup unit test.
172  *
173  * Create a 1 s periodic timer.
174  * Start the network driver.
175  *
176  * @handle:	handle of the loaded image
177  * @systable:	system table
178  * @return:	EFI_ST_SUCCESS for success
179  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)180 static int setup(const efi_handle_t handle,
181 		 const struct efi_system_table *systable)
182 {
183 	efi_status_t ret;
184 
185 	boottime = systable->boottime;
186 
187 	/*
188 	 * Create a timer event.
189 	 */
190 	ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
191 				     &timer);
192 	if (ret != EFI_SUCCESS) {
193 		efi_st_error("Failed to create event\n");
194 		return EFI_ST_FAILURE;
195 	}
196 	/*
197 	 * Set timer period to 1s.
198 	 */
199 	ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
200 	if (ret != EFI_SUCCESS) {
201 		efi_st_error("Failed to set timer\n");
202 		return EFI_ST_FAILURE;
203 	}
204 	/*
205 	 * Find an interface implementing the SNP protocol.
206 	 */
207 	ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
208 	if (ret != EFI_SUCCESS) {
209 		net = NULL;
210 		efi_st_error("Failed to locate simple network protocol\n");
211 		return EFI_ST_FAILURE;
212 	}
213 	/*
214 	 * Check hardware address size.
215 	 */
216 	if (!net->mode) {
217 		efi_st_error("Mode not provided\n");
218 		return EFI_ST_FAILURE;
219 	}
220 	if (net->mode->hwaddr_size != ARP_HLEN) {
221 		efi_st_error("HwAddressSize = %u, expected %u\n",
222 			     net->mode->hwaddr_size, ARP_HLEN);
223 		return EFI_ST_FAILURE;
224 	}
225 	/*
226 	 * Check that WaitForPacket event exists.
227 	 */
228 	if (!net->wait_for_packet) {
229 		efi_st_error("WaitForPacket event missing\n");
230 		return EFI_ST_FAILURE;
231 	}
232 	if (net->mode->state == EFI_NETWORK_INITIALIZED) {
233 		/*
234 		 * Shut down network adapter.
235 		 */
236 		ret = net->shutdown(net);
237 		if (ret != EFI_SUCCESS) {
238 			efi_st_error("Failed to shut down network adapter\n");
239 			return EFI_ST_FAILURE;
240 		}
241 	}
242 	if (net->mode->state == EFI_NETWORK_STARTED) {
243 		/*
244 		 * Stop network adapter.
245 		 */
246 		ret = net->stop(net);
247 		if (ret != EFI_SUCCESS) {
248 			efi_st_error("Failed to stop network adapter\n");
249 			return EFI_ST_FAILURE;
250 		}
251 	}
252 	/*
253 	 * Start network adapter.
254 	 */
255 	ret = net->start(net);
256 	if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
257 		efi_st_error("Failed to start network adapter\n");
258 		return EFI_ST_FAILURE;
259 	}
260 	if (net->mode->state != EFI_NETWORK_STARTED) {
261 		efi_st_error("Failed to start network adapter\n");
262 		return EFI_ST_FAILURE;
263 	}
264 	/*
265 	 * Initialize network adapter.
266 	 */
267 	ret = net->initialize(net, 0, 0);
268 	if (ret != EFI_SUCCESS) {
269 		efi_st_error("Failed to initialize network adapter\n");
270 		return EFI_ST_FAILURE;
271 	}
272 	if (net->mode->state != EFI_NETWORK_INITIALIZED) {
273 		efi_st_error("Failed to initialize network adapter\n");
274 		return EFI_ST_FAILURE;
275 	}
276 	return EFI_ST_SUCCESS;
277 }
278 
279 /*
280  * Execute unit test.
281  *
282  * A DHCP discover message is sent. The test is successful if a
283  * DHCP reply is received within 10 seconds.
284  *
285  * @return:	EFI_ST_SUCCESS for success
286  */
execute(void)287 static int execute(void)
288 {
289 	efi_status_t ret;
290 	struct efi_event *events[2];
291 	efi_uintn_t index;
292 	union {
293 		struct dhcp p;
294 		u8 b[PKTSIZE];
295 	} buffer;
296 	struct efi_mac_address srcaddr;
297 	struct efi_mac_address destaddr;
298 	size_t buffer_size;
299 	u8 *addr;
300 
301 	/*
302 	 * The timeout is to occur after 10 s.
303 	 */
304 	unsigned int timeout = 10;
305 
306 	/* Setup may have failed */
307 	if (!net || !timer) {
308 		efi_st_error("Cannot execute test after setup failure\n");
309 		return EFI_ST_FAILURE;
310 	}
311 
312 	/*
313 	 * Send DHCP discover message
314 	 */
315 	ret = send_dhcp_discover();
316 	if (ret != EFI_SUCCESS)
317 		return EFI_ST_FAILURE;
318 
319 	/*
320 	 * If we would call WaitForEvent only with the WaitForPacket event,
321 	 * our code would block until a packet is received which might never
322 	 * occur. By calling WaitFor event with both a timer event and the
323 	 * WaitForPacket event we can escape this blocking situation.
324 	 *
325 	 * If the timer event occurs before we have received a DHCP reply
326 	 * a further DHCP discover message is sent.
327 	 */
328 	events[0] = timer;
329 	events[1] = net->wait_for_packet;
330 	for (;;) {
331 		u32 int_status;
332 
333 		/*
334 		 * Wait for packet to be received or timer event.
335 		 */
336 		boottime->wait_for_event(2, events, &index);
337 		if (index == 0) {
338 			/*
339 			 * The timer event occurred. Check for timeout.
340 			 */
341 			--timeout;
342 			if (!timeout) {
343 				efi_st_error("Timeout occurred\n");
344 				return EFI_ST_FAILURE;
345 			}
346 			/*
347 			 * Send further DHCP discover message
348 			 */
349 			ret = send_dhcp_discover();
350 			if (ret != EFI_SUCCESS)
351 				return EFI_ST_FAILURE;
352 			continue;
353 		}
354 		/*
355 		 * Receive packet
356 		 */
357 		buffer_size = sizeof(buffer);
358 		ret = net->get_status(net, &int_status, NULL);
359 		if (ret != EFI_SUCCESS) {
360 			efi_st_error("Failed to get status");
361 			return EFI_ST_FAILURE;
362 		}
363 		if (!(int_status & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT)) {
364 			efi_st_error("RX interrupt not set");
365 			return EFI_ST_FAILURE;
366 		}
367 		ret = net->receive(net, NULL, &buffer_size, &buffer,
368 				   &srcaddr, &destaddr, NULL);
369 		if (ret != EFI_SUCCESS) {
370 			efi_st_error("Failed to receive packet");
371 			return EFI_ST_FAILURE;
372 		}
373 		/*
374 		 * Check the packet is meant for this system.
375 		 * Unfortunately QEMU ignores the broadcast flag.
376 		 * So we have to check for broadcasts too.
377 		 */
378 		if (memcmp(&destaddr, &net->mode->current_address, ARP_HLEN) &&
379 		    memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
380 			continue;
381 		/*
382 		 * Check this is a DHCP reply
383 		 */
384 		if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
385 		    buffer.p.ip_udp.ip_hl_v != 0x45 ||
386 		    buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
387 		    buffer.p.ip_udp.udp_src != ntohs(67) ||
388 		    buffer.p.ip_udp.udp_dst != ntohs(68) ||
389 		    buffer.p.dhcp_hdr.op != BOOTREPLY)
390 			continue;
391 		/*
392 		 * We successfully received a DHCP reply.
393 		 */
394 		break;
395 	}
396 
397 	/*
398 	 * Write a log message.
399 	 */
400 	addr = (u8 *)&buffer.p.ip_udp.ip_src;
401 	efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
402 		      addr[0], addr[1], addr[2], addr[3], &srcaddr);
403 	if (!memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
404 		efi_st_printf("as broadcast message.\n");
405 	else
406 		efi_st_printf("as unicast message.\n");
407 
408 	return EFI_ST_SUCCESS;
409 }
410 
411 /*
412  * Tear down unit test.
413  *
414  * Close the timer event created in setup.
415  * Shut down the network adapter.
416  *
417  * @return:	EFI_ST_SUCCESS for success
418  */
teardown(void)419 static int teardown(void)
420 {
421 	efi_status_t ret;
422 	int exit_status = EFI_ST_SUCCESS;
423 
424 	if (timer) {
425 		/*
426 		 * Stop timer.
427 		 */
428 		ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
429 		if (ret != EFI_SUCCESS) {
430 			efi_st_error("Failed to stop timer");
431 			exit_status = EFI_ST_FAILURE;
432 		}
433 		/*
434 		 * Close timer event.
435 		 */
436 		ret = boottime->close_event(timer);
437 		if (ret != EFI_SUCCESS) {
438 			efi_st_error("Failed to close event");
439 			exit_status = EFI_ST_FAILURE;
440 		}
441 	}
442 	if (net) {
443 		/*
444 		 * Shut down network adapter.
445 		 */
446 		ret = net->shutdown(net);
447 		if (ret != EFI_SUCCESS) {
448 			efi_st_error("Failed to shut down network adapter\n");
449 			exit_status = EFI_ST_FAILURE;
450 		}
451 		if (net->mode->state != EFI_NETWORK_STARTED) {
452 			efi_st_error("Failed to shutdown network adapter\n");
453 			return EFI_ST_FAILURE;
454 		}
455 		/*
456 		 * Stop network adapter.
457 		 */
458 		ret = net->stop(net);
459 		if (ret != EFI_SUCCESS) {
460 			efi_st_error("Failed to stop network adapter\n");
461 			exit_status = EFI_ST_FAILURE;
462 		}
463 		if (net->mode->state != EFI_NETWORK_STOPPED) {
464 			efi_st_error("Failed to stop network adapter\n");
465 			return EFI_ST_FAILURE;
466 		}
467 	}
468 
469 	return exit_status;
470 }
471 
472 EFI_UNIT_TEST(snp) = {
473 	.name = "simple network protocol",
474 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
475 	.setup = setup,
476 	.execute = execute,
477 	.teardown = teardown,
478 #ifdef CONFIG_SANDBOX
479 	/*
480 	 * Running this test on the sandbox requires setting environment
481 	 * variable ethact to a network interface connected to a DHCP server and
482 	 * ethrotate to 'no'.
483 	 */
484 	.on_request = true,
485 #endif
486 };
487