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