]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiDhcp6.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp6.c
1 /** @file
2 iSCSI DHCP6 related configuration routines.
3
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "IScsiImpl.h"
10
11 /**
12 Extract the Root Path option and get the required target information from
13 Boot File Uniform Resource Locator (URL) Option.
14
15 @param[in] RootPath The RootPath string.
16 @param[in] Length Length of the RootPath option payload.
17 @param[in, out] ConfigData The iSCSI session configuration data read from
18 nonvolatile device.
19
20 @retval EFI_SUCCESS All required information is extracted from the
21 RootPath option.
22 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
23 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
24 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
25
26 **/
27 EFI_STATUS
28 IScsiDhcp6ExtractRootPath (
29 IN CHAR8 *RootPath,
30 IN UINT16 Length,
31 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
32 )
33 {
34 EFI_STATUS Status;
35 UINT16 IScsiRootPathIdLen;
36 CHAR8 *TmpStr;
37 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
38 ISCSI_ROOT_PATH_FIELD *Field;
39 UINT32 FieldIndex;
40 UINT8 Index;
41 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
42 EFI_IP_ADDRESS Ip;
43 UINT8 IpMode;
44
45 ConfigNvData = &ConfigData->SessionConfigData;
46 ConfigNvData->DnsMode = FALSE;
47 //
48 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
49 //
50 IScsiRootPathIdLen = (UINT16)AsciiStrLen (ISCSI_ROOT_PATH_ID);
51
52 if ((Length <= IScsiRootPathIdLen) ||
53 (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0))
54 {
55 return EFI_NOT_FOUND;
56 }
57
58 //
59 // Skip the iSCSI RootPath ID "iscsi:".
60 //
61 RootPath = RootPath + IScsiRootPathIdLen;
62 Length = (UINT16)(Length - IScsiRootPathIdLen);
63
64 TmpStr = (CHAR8 *)AllocatePool (Length + 1);
65 if (TmpStr == NULL) {
66 return EFI_OUT_OF_RESOURCES;
67 }
68
69 CopyMem (TmpStr, RootPath, Length);
70 TmpStr[Length] = '\0';
71
72 Index = 0;
73 FieldIndex = 0;
74 ZeroMem (&Fields[0], sizeof (Fields));
75
76 //
77 // Extract SERVERNAME field in the Root Path option.
78 //
79 if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {
80 //
81 // The servername is expressed as domain name.
82 //
83 ConfigNvData->DnsMode = TRUE;
84 } else {
85 Index++;
86 }
87
88 Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];
89
90 if (!ConfigNvData->DnsMode) {
91 while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) {
92 Index++;
93 }
94
95 //
96 // Skip ']' and ':'.
97 //
98 TmpStr[Index] = '\0';
99 Index += 2;
100 } else {
101 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
102 Index++;
103 }
104
105 //
106 // Skip ':'.
107 //
108 TmpStr[Index] = '\0';
109 Index += 1;
110 }
111
112 Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8)AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);
113
114 //
115 // Extract others fields in the Root Path option string.
116 //
117 for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
118 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
119 Fields[FieldIndex].Str = &TmpStr[Index];
120 }
121
122 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
123 Index++;
124 }
125
126 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
127 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
128 TmpStr[Index] = '\0';
129 Index++;
130 }
131
132 if (Fields[FieldIndex].Str != NULL) {
133 Fields[FieldIndex].Len = (UINT8)AsciiStrLen (Fields[FieldIndex].Str);
134 }
135 }
136 }
137
138 if (FieldIndex != RP_FIELD_IDX_MAX) {
139 Status = EFI_INVALID_PARAMETER;
140 goto ON_EXIT;
141 }
142
143 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
144 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
145 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
146 )
147 {
148 Status = EFI_INVALID_PARAMETER;
149 goto ON_EXIT;
150 }
151
152 //
153 // Get the IP address of the target.
154 //
155 Field = &Fields[RP_FIELD_IDX_SERVERNAME];
156 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
157 IpMode = ConfigNvData->IpMode;
158 } else {
159 IpMode = ConfigData->AutoConfigureMode;
160 }
161
162 //
163 // Server name is expressed as domain name, just save it.
164 //
165 if (ConfigNvData->DnsMode) {
166 if ((Field->Len + 2) > sizeof (ConfigNvData->TargetUrl)) {
167 return EFI_INVALID_PARAMETER;
168 }
169
170 CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
171 ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
172 } else {
173 ZeroMem (&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
174 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
175 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
176
177 if (EFI_ERROR (Status)) {
178 goto ON_EXIT;
179 }
180 }
181
182 //
183 // Check the protocol type.
184 //
185 Field = &Fields[RP_FIELD_IDX_PROTOCOL];
186 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
187 Status = EFI_INVALID_PARAMETER;
188 goto ON_EXIT;
189 }
190
191 //
192 // Get the port of the iSCSI target.
193 //
194 Field = &Fields[RP_FIELD_IDX_PORT];
195 if (Field->Str != NULL) {
196 ConfigNvData->TargetPort = (UINT16)AsciiStrDecimalToUintn (Field->Str);
197 } else {
198 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
199 }
200
201 //
202 // Get the LUN.
203 //
204 Field = &Fields[RP_FIELD_IDX_LUN];
205 if (Field->Str != NULL) {
206 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
207 if (EFI_ERROR (Status)) {
208 goto ON_EXIT;
209 }
210 } else {
211 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
212 }
213
214 //
215 // Get the target iSCSI Name.
216 //
217 Field = &Fields[RP_FIELD_IDX_TARGETNAME];
218
219 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
220 Status = EFI_INVALID_PARAMETER;
221 goto ON_EXIT;
222 }
223
224 //
225 // Validate the iSCSI name.
226 //
227 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
228 if (EFI_ERROR (Status)) {
229 goto ON_EXIT;
230 }
231
232 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
233
234 ON_EXIT:
235
236 FreePool (TmpStr);
237
238 return Status;
239 }
240
241 /**
242 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
243 instance to intercept events that occurs in the DHCPv6 Information Request
244 exchange process.
245
246 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
247 is used to configure this callback function.
248 @param[in] Context Pointer to the context that is initialized in
249 the EFI_DHCP6_PROTOCOL.InfoRequest().
250 @param[in] Packet Pointer to Reply packet that has been received.
251 The EFI DHCPv6 Protocol instance is responsible
252 for freeing the buffer.
253
254 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
255 Information Request exchange process.
256 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
257 Information Request exchange process.
258 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
259 the Information Request exchange process.
260 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
261 the Information Request exchange process because some
262 request information are not received.
263
264 **/
265 EFI_STATUS
266 EFIAPI
267 IScsiDhcp6ParseReply (
268 IN EFI_DHCP6_PROTOCOL *This,
269 IN VOID *Context,
270 IN EFI_DHCP6_PACKET *Packet
271 )
272 {
273 EFI_STATUS Status;
274 UINT32 Index;
275 UINT32 OptionCount;
276 EFI_DHCP6_PACKET_OPTION *BootFileOpt;
277 EFI_DHCP6_PACKET_OPTION **OptionList;
278 ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;
279 UINT16 ParaLen;
280
281 OptionCount = 0;
282 BootFileOpt = NULL;
283
284 Status = This->Parse (This, Packet, &OptionCount, NULL);
285 if (Status != EFI_BUFFER_TOO_SMALL) {
286 return EFI_NOT_READY;
287 }
288
289 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
290 if (OptionList == NULL) {
291 return EFI_NOT_READY;
292 }
293
294 Status = This->Parse (This, Packet, &OptionCount, OptionList);
295 if (EFI_ERROR (Status)) {
296 Status = EFI_NOT_READY;
297 goto Exit;
298 }
299
300 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *)Context;
301
302 for (Index = 0; Index < OptionCount; Index++) {
303 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
304 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);
305
306 //
307 // Get DNS server addresses from this reply packet.
308 //
309 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {
310 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
311 Status = EFI_UNSUPPORTED;
312 goto Exit;
313 }
314
315 //
316 // Primary DNS server address.
317 //
318 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));
319
320 if (OptionList[Index]->OpLen > 16) {
321 //
322 // Secondary DNS server address
323 //
324 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));
325 }
326 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {
327 //
328 // The server sends this option to inform the client about an URL to a boot file.
329 //
330 BootFileOpt = OptionList[Index];
331 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARAM) {
332 //
333 // The server sends this option to inform the client about DHCP6 server address.
334 //
335 if (OptionList[Index]->OpLen < 18) {
336 Status = EFI_UNSUPPORTED;
337 goto Exit;
338 }
339
340 //
341 // Check param-len 1, should be 16 bytes.
342 //
343 CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16));
344 if (NTOHS (ParaLen) != 16) {
345 Status = EFI_UNSUPPORTED;
346 goto Exit;
347 }
348
349 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS));
350 }
351 }
352
353 if (BootFileOpt == NULL) {
354 Status = EFI_UNSUPPORTED;
355 goto Exit;
356 }
357
358 //
359 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
360 //
361 Status = IScsiDhcp6ExtractRootPath (
362 (CHAR8 *)BootFileOpt->Data,
363 BootFileOpt->OpLen,
364 ConfigData
365 );
366
367 Exit:
368
369 FreePool (OptionList);
370 return Status;
371 }
372
373 /**
374 Parse the DHCP ACK to get the address configuration and DNS information.
375
376 @param[in] Image The handle of the driver image.
377 @param[in] Controller The handle of the controller;
378 @param[in, out] ConfigData The attempt configuration data.
379
380 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
381 @retval EFI_NO_MAPPING DHCP failed to acquire address and other
382 information.
383 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
384 @retval EFI_DEVICE_ERROR Some unexpected error occurred.
385 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
386 operation.
387 @retval EFI_NO_MEDIA There was a media error.
388
389 **/
390 EFI_STATUS
391 IScsiDoDhcp6 (
392 IN EFI_HANDLE Image,
393 IN EFI_HANDLE Controller,
394 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
395 )
396 {
397 EFI_HANDLE Dhcp6Handle;
398 EFI_DHCP6_PROTOCOL *Dhcp6;
399 EFI_STATUS Status;
400 EFI_STATUS TimerStatus;
401 EFI_DHCP6_PACKET_OPTION *Oro;
402 EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
403 EFI_EVENT Timer;
404 EFI_STATUS MediaStatus;
405
406 //
407 // Check media status before doing DHCP.
408 //
409 MediaStatus = EFI_SUCCESS;
410 NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus);
411 if (MediaStatus != EFI_SUCCESS) {
412 AsciiPrint ("\n Error: Could not detect network connection.\n");
413 return EFI_NO_MEDIA;
414 }
415
416 //
417 // iSCSI will only request target info from DHCPv6 server.
418 //
419 if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {
420 return EFI_SUCCESS;
421 }
422
423 Dhcp6Handle = NULL;
424 Dhcp6 = NULL;
425 Oro = NULL;
426 Timer = NULL;
427
428 //
429 // Create a DHCP6 child instance and get the protocol.
430 //
431 Status = NetLibCreateServiceChild (
432 Controller,
433 Image,
434 &gEfiDhcp6ServiceBindingProtocolGuid,
435 &Dhcp6Handle
436 );
437 if (EFI_ERROR (Status)) {
438 return Status;
439 }
440
441 Status = gBS->OpenProtocol (
442 Dhcp6Handle,
443 &gEfiDhcp6ProtocolGuid,
444 (VOID **)&Dhcp6,
445 Image,
446 Controller,
447 EFI_OPEN_PROTOCOL_BY_DRIVER
448 );
449 if (EFI_ERROR (Status)) {
450 goto ON_EXIT;
451 }
452
453 Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5);
454 if (Oro == NULL) {
455 Status = EFI_OUT_OF_RESOURCES;
456 goto ON_EXIT;
457 }
458
459 //
460 // Ask the server to reply with DNS and Boot File URL options by info request.
461 // All members in EFI_DHCP6_PACKET_OPTION are in network order.
462 //
463 Oro->OpCode = HTONS (DHCP6_OPT_ORO);
464 Oro->OpLen = HTONS (2 * 3);
465 Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;
466 Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;
467 Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARAM;
468
469 InfoReqReXmit.Irt = 4;
470 InfoReqReXmit.Mrc = 1;
471 InfoReqReXmit.Mrt = 10;
472 InfoReqReXmit.Mrd = 30;
473
474 Status = Dhcp6->InfoRequest (
475 Dhcp6,
476 TRUE,
477 Oro,
478 0,
479 NULL,
480 &InfoReqReXmit,
481 NULL,
482 IScsiDhcp6ParseReply,
483 ConfigData
484 );
485 if (Status == EFI_NO_MAPPING) {
486 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
487 if (EFI_ERROR (Status)) {
488 goto ON_EXIT;
489 }
490
491 Status = gBS->SetTimer (
492 Timer,
493 TimerRelative,
494 ISCSI_GET_MAPPING_TIMEOUT
495 );
496
497 if (EFI_ERROR (Status)) {
498 goto ON_EXIT;
499 }
500
501 do {
502 TimerStatus = gBS->CheckEvent (Timer);
503
504 if (!EFI_ERROR (TimerStatus)) {
505 Status = Dhcp6->InfoRequest (
506 Dhcp6,
507 TRUE,
508 Oro,
509 0,
510 NULL,
511 &InfoReqReXmit,
512 NULL,
513 IScsiDhcp6ParseReply,
514 ConfigData
515 );
516 }
517 } while (TimerStatus == EFI_NOT_READY);
518 }
519
520 ON_EXIT:
521
522 if (Oro != NULL) {
523 FreePool (Oro);
524 }
525
526 if (Timer != NULL) {
527 gBS->CloseEvent (Timer);
528 }
529
530 if (Dhcp6 != NULL) {
531 gBS->CloseProtocol (
532 Dhcp6Handle,
533 &gEfiDhcp6ProtocolGuid,
534 Image,
535 Controller
536 );
537 }
538
539 NetLibDestroyServiceChild (
540 Controller,
541 Image,
542 &gEfiDhcp6ServiceBindingProtocolGuid,
543 Dhcp6Handle
544 );
545
546 return Status;
547 }