]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiDhcp6.c
NetworkPkg: Clean up source files
[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 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "IScsiImpl.h"
16
17
18 /**
19 Extract the Root Path option and get the required target information from
20 Boot File Uniform Resource Locator (URL) Option.
21
22 @param[in] RootPath The RootPath string.
23 @param[in] Length Length of the RootPath option payload.
24 @param[in, out] ConfigData The iSCSI session configuration data read from
25 nonvolatile device.
26
27 @retval EFI_SUCCESS All required information is extracted from the
28 RootPath option.
29 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
30 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
31 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
32
33 **/
34 EFI_STATUS
35 IScsiDhcp6ExtractRootPath (
36 IN CHAR8 *RootPath,
37 IN UINT16 Length,
38 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
39 )
40 {
41 EFI_STATUS Status;
42 UINT16 IScsiRootPathIdLen;
43 CHAR8 *TmpStr;
44 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
45 ISCSI_ROOT_PATH_FIELD *Field;
46 UINT32 FieldIndex;
47 UINT8 Index;
48 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
49 EFI_IP_ADDRESS Ip;
50 UINT8 IpMode;
51
52 ConfigNvData = &ConfigData->SessionConfigData;
53 ConfigNvData->DnsMode = FALSE;
54 //
55 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
56 //
57 IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID);
58
59 if ((Length <= IScsiRootPathIdLen) ||
60 (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
61 return EFI_NOT_FOUND;
62 }
63 //
64 // Skip the iSCSI RootPath ID "iscsi:".
65 //
66 RootPath = RootPath + IScsiRootPathIdLen;
67 Length = (UINT16) (Length - IScsiRootPathIdLen);
68
69 TmpStr = (CHAR8 *) AllocatePool (Length + 1);
70 if (TmpStr == NULL) {
71 return EFI_OUT_OF_RESOURCES;
72 }
73
74 CopyMem (TmpStr, RootPath, Length);
75 TmpStr[Length] = '\0';
76
77 Index = 0;
78 FieldIndex = 0;
79 ZeroMem (&Fields[0], sizeof (Fields));
80
81 //
82 // Extract SERVERNAME field in the Root Path option.
83 //
84 if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {
85 //
86 // The servername is expressed as domain name.
87 //
88 ConfigNvData->DnsMode = TRUE;
89 } else {
90 Index++;
91 }
92
93 Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];
94
95 if (!ConfigNvData->DnsMode) {
96 while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER)&& (Index < Length)) {
97 Index++;
98 }
99
100 //
101 // Skip ']' and ':'.
102 //
103 TmpStr[Index] = '\0';
104 Index += 2;
105 } else {
106 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
107 Index++;
108 }
109 //
110 // Skip ':'.
111 //
112 TmpStr[Index] = '\0';
113 Index += 1;
114 }
115
116 Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);
117
118 //
119 // Extract others fields in the Root Path option string.
120 //
121 for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
122
123 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
124 Fields[FieldIndex].Str = &TmpStr[Index];
125 }
126
127 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
128 Index++;
129 }
130
131 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
132 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
133 TmpStr[Index] = '\0';
134 Index++;
135 }
136
137 if (Fields[FieldIndex].Str != NULL) {
138 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
139 }
140 }
141 }
142
143 if (FieldIndex != RP_FIELD_IDX_MAX) {
144 Status = EFI_INVALID_PARAMETER;
145 goto ON_EXIT;
146 }
147
148 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
149 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
150 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
151 ) {
152
153 Status = EFI_INVALID_PARAMETER;
154 goto ON_EXIT;
155 }
156 //
157 // Get the IP address of the target.
158 //
159 Field = &Fields[RP_FIELD_IDX_SERVERNAME];
160 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
161 IpMode = ConfigNvData->IpMode;
162 } else {
163 IpMode = ConfigData->AutoConfigureMode;
164 }
165
166 //
167 // Server name is expressed as domain name, just save it.
168 //
169 if (ConfigNvData->DnsMode) {
170 if (Field->Len > sizeof (ConfigNvData->TargetUrl)) {
171 return EFI_INVALID_PARAMETER;
172 }
173 CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
174 ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
175 } else {
176 ZeroMem(&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
177 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
178 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
179
180 if (EFI_ERROR (Status)) {
181 goto ON_EXIT;
182 }
183 }
184
185 //
186 // Check the protocol type.
187 //
188 Field = &Fields[RP_FIELD_IDX_PROTOCOL];
189 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
190 Status = EFI_INVALID_PARAMETER;
191 goto ON_EXIT;
192 }
193 //
194 // Get the port of the iSCSI target.
195 //
196 Field = &Fields[RP_FIELD_IDX_PORT];
197 if (Field->Str != NULL) {
198 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
199 } else {
200 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
201 }
202 //
203 // Get the LUN.
204 //
205 Field = &Fields[RP_FIELD_IDX_LUN];
206 if (Field->Str != NULL) {
207 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
208 if (EFI_ERROR (Status)) {
209 goto ON_EXIT;
210 }
211 } else {
212 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
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 // Validate the iSCSI name.
225 //
226 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
227 if (EFI_ERROR (Status)) {
228 goto ON_EXIT;
229 }
230
231 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
232
233 ON_EXIT:
234
235 FreePool (TmpStr);
236
237 return Status;
238 }
239
240 /**
241 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
242 instance to intercept events that occurs in the DHCPv6 Information Request
243 exchange process.
244
245 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
246 is used to configure this callback function.
247 @param[in] Context Pointer to the context that is initialized in
248 the EFI_DHCP6_PROTOCOL.InfoRequest().
249 @param[in] Packet Pointer to Reply packet that has been received.
250 The EFI DHCPv6 Protocol instance is responsible
251 for freeing the buffer.
252
253 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
254 Information Request exchange process.
255 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
256 Information Request exchange process.
257 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
258 the Information Request exchange process.
259 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
260 the Information Request exchange process because some
261 request information are not received.
262
263 **/
264 EFI_STATUS
265 EFIAPI
266 IScsiDhcp6ParseReply (
267 IN EFI_DHCP6_PROTOCOL *This,
268 IN VOID *Context,
269 IN EFI_DHCP6_PACKET *Packet
270 )
271 {
272 EFI_STATUS Status;
273 UINT32 Index;
274 UINT32 OptionCount;
275 EFI_DHCP6_PACKET_OPTION *BootFileOpt;
276 EFI_DHCP6_PACKET_OPTION **OptionList;
277 ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;
278 UINT16 ParaLen;
279
280 OptionCount = 0;
281 BootFileOpt = NULL;
282
283 Status = This->Parse (This, Packet, &OptionCount, NULL);
284 if (Status != EFI_BUFFER_TOO_SMALL) {
285 return EFI_NOT_READY;
286 }
287
288 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
289 if (OptionList == NULL) {
290 return EFI_NOT_READY;
291 }
292
293 Status = This->Parse (This, Packet, &OptionCount, OptionList);
294 if (EFI_ERROR (Status)) {
295 Status = EFI_NOT_READY;
296 goto Exit;
297 }
298
299 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;
300
301 for (Index = 0; Index < OptionCount; Index++) {
302 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
303 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);
304
305 //
306 // Get DNS server addresses from this reply packet.
307 //
308 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {
309
310 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
311 Status = EFI_UNSUPPORTED;
312 goto Exit;
313 }
314 //
315 // Primary DNS server address.
316 //
317 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));
318
319 if (OptionList[Index]->OpLen > 16) {
320 //
321 // Secondary DNS server address
322 //
323 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));
324 }
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 // Check param-len 1, should be 16 bytes.
341 //
342 CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16));
343 if (NTOHS (ParaLen) != 16) {
344 Status = EFI_UNSUPPORTED;
345 goto Exit;
346 }
347
348 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS));
349 }
350 }
351
352 if (BootFileOpt == NULL) {
353 Status = EFI_UNSUPPORTED;
354 goto Exit;
355 }
356
357 //
358 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
359 //
360 Status = IScsiDhcp6ExtractRootPath (
361 (CHAR8 *) BootFileOpt->Data,
362 BootFileOpt->OpLen,
363 ConfigData
364 );
365
366 Exit:
367
368 FreePool (OptionList);
369 return Status;
370 }
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
503 TimerStatus = gBS->CheckEvent (Timer);
504
505 if (!EFI_ERROR (TimerStatus)) {
506 Status = Dhcp6->InfoRequest (
507 Dhcp6,
508 TRUE,
509 Oro,
510 0,
511 NULL,
512 &InfoReqReXmit,
513 NULL,
514 IScsiDhcp6ParseReply,
515 ConfigData
516 );
517 }
518
519 } while (TimerStatus == EFI_NOT_READY);
520
521 }
522
523 ON_EXIT:
524
525 if (Oro != NULL) {
526 FreePool (Oro);
527 }
528
529 if (Timer != NULL) {
530 gBS->CloseEvent (Timer);
531 }
532
533 if (Dhcp6 != NULL) {
534 gBS->CloseProtocol (
535 Dhcp6Handle,
536 &gEfiDhcp6ProtocolGuid,
537 Image,
538 Controller
539 );
540 }
541
542 NetLibDestroyServiceChild (
543 Controller,
544 Image,
545 &gEfiDhcp6ServiceBindingProtocolGuid,
546 Dhcp6Handle
547 );
548
549 return Status;
550 }
551