1 /* 2 * efi_selftest_snp 3 * 4 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 * 8 * This unit test covers the Simple Network Protocol as well as 9 * the CopyMem and SetMem boottime services. 10 * 11 * A DHCP discover message is sent. The test is successful if a 12 * DHCP reply is received. 13 * 14 * TODO: Once ConnectController and DisconnectController are implemented 15 * we should connect our code as controller. 16 */ 17 18 #include <efi_selftest.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_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 */ 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 */ 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 */ 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 /* 233 * Initialize network adapter. 234 */ 235 ret = net->initialize(net, 0, 0); 236 if (ret != EFI_SUCCESS) { 237 efi_st_error("Failed to initialize network adapter\n"); 238 return EFI_ST_FAILURE; 239 } 240 /* 241 * Start network adapter. 242 */ 243 ret = net->start(net); 244 if (ret != EFI_SUCCESS) { 245 efi_st_error("Failed to start network adapter\n"); 246 return EFI_ST_FAILURE; 247 } 248 return EFI_ST_SUCCESS; 249 } 250 251 /* 252 * Execute unit test. 253 * 254 * A DHCP discover message is sent. The test is successful if a 255 * DHCP reply is received within 10 seconds. 256 * 257 * @return: EFI_ST_SUCCESS for success 258 */ 259 static int execute(void) 260 { 261 efi_status_t ret; 262 struct efi_event *events[2]; 263 size_t index; 264 union { 265 struct dhcp p; 266 u8 b[PKTSIZE]; 267 } buffer; 268 struct efi_mac_address srcaddr; 269 struct efi_mac_address destaddr; 270 size_t buffer_size; 271 u8 *addr; 272 /* 273 * The timeout is to occur after 10 s. 274 */ 275 unsigned int timeout = 10; 276 277 /* Setup may have failed */ 278 if (!net || !timer) { 279 efi_st_error("Cannot execute test after setup failure\n"); 280 return EFI_ST_FAILURE; 281 } 282 283 /* 284 * Send DHCP discover message 285 */ 286 ret = send_dhcp_discover(); 287 if (ret != EFI_SUCCESS) 288 return EFI_ST_FAILURE; 289 290 /* 291 * If we would call WaitForEvent only with the WaitForPacket event, 292 * our code would block until a packet is received which might never 293 * occur. By calling WaitFor event with both a timer event and the 294 * WaitForPacket event we can escape this blocking situation. 295 * 296 * If the timer event occurs before we have received a DHCP reply 297 * a further DHCP discover message is sent. 298 */ 299 events[0] = timer; 300 events[1] = net->wait_for_packet; 301 for (;;) { 302 /* 303 * Wait for packet to be received or timer event. 304 */ 305 boottime->wait_for_event(2, events, &index); 306 if (index == 0) { 307 /* 308 * The timer event occurred. Check for timeout. 309 */ 310 --timeout; 311 if (!timeout) { 312 efi_st_error("Timeout occurred\n"); 313 return EFI_ST_FAILURE; 314 } 315 /* 316 * Send further DHCP discover message 317 */ 318 ret = send_dhcp_discover(); 319 if (ret != EFI_SUCCESS) 320 return EFI_ST_FAILURE; 321 continue; 322 } 323 /* 324 * Receive packet 325 */ 326 buffer_size = sizeof(buffer); 327 net->receive(net, NULL, &buffer_size, &buffer, 328 &srcaddr, &destaddr, NULL); 329 if (ret != EFI_SUCCESS) { 330 efi_st_error("Failed to receive packet"); 331 return EFI_ST_FAILURE; 332 } 333 /* 334 * Check the packet is meant for this system. 335 * Unfortunately QEMU ignores the broadcast flag. 336 * So we have to check for broadcasts too. 337 */ 338 if (efi_st_memcmp(&destaddr, &net->mode->current_address, 339 ARP_HLEN) && 340 efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) 341 continue; 342 /* 343 * Check this is a DHCP reply 344 */ 345 if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) || 346 buffer.p.ip_udp.ip_hl_v != 0x45 || 347 buffer.p.ip_udp.ip_p != IPPROTO_UDP || 348 buffer.p.ip_udp.udp_src != ntohs(67) || 349 buffer.p.ip_udp.udp_dst != ntohs(68) || 350 buffer.p.dhcp_hdr.op != BOOTREPLY) 351 continue; 352 /* 353 * We successfully received a DHCP reply. 354 */ 355 break; 356 } 357 358 /* 359 * Write a log message. 360 */ 361 addr = (u8 *)&buffer.p.ip_udp.ip_src; 362 efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ", 363 addr[0], addr[1], addr[2], addr[3], &srcaddr); 364 if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) 365 efi_st_printf("as broadcast message.\n"); 366 else 367 efi_st_printf("as unicast message.\n"); 368 369 return EFI_ST_SUCCESS; 370 } 371 372 /* 373 * Tear down unit test. 374 * 375 * Close the timer event created in setup. 376 * Shut down the network adapter. 377 * 378 * @return: EFI_ST_SUCCESS for success 379 */ 380 static int teardown(void) 381 { 382 efi_status_t ret; 383 int exit_status = EFI_ST_SUCCESS; 384 385 if (timer) { 386 /* 387 * Stop timer. 388 */ 389 ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0); 390 if (ret != EFI_SUCCESS) { 391 efi_st_error("Failed to stop timer"); 392 exit_status = EFI_ST_FAILURE; 393 } 394 /* 395 * Close timer event. 396 */ 397 ret = boottime->close_event(timer); 398 if (ret != EFI_SUCCESS) { 399 efi_st_error("Failed to close event"); 400 exit_status = EFI_ST_FAILURE; 401 } 402 } 403 if (net) { 404 /* 405 * Stop network adapter. 406 */ 407 ret = net->stop(net); 408 if (ret != EFI_SUCCESS) { 409 efi_st_error("Failed to stop network adapter\n"); 410 exit_status = EFI_ST_FAILURE; 411 } 412 /* 413 * Shut down network adapter. 414 */ 415 ret = net->shutdown(net); 416 if (ret != EFI_SUCCESS) { 417 efi_st_error("Failed to shut down network adapter\n"); 418 exit_status = EFI_ST_FAILURE; 419 } 420 } 421 422 return exit_status; 423 } 424 425 EFI_UNIT_TEST(snp) = { 426 .name = "simple network protocol", 427 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, 428 .setup = setup, 429 .execute = execute, 430 .teardown = teardown, 431 }; 432