VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/IScsiDxe/IScsiDhcp.c@ 77662

Last change on this file since 77662 was 77662, checked in by vboxsync, 6 years ago

EFI: First step in UDK2018 merge. Does not build yet.

  • Property svn:eol-style set to native
File size: 16.3 KB
Line 
1/** @file
2 iSCSI DHCP4 related configuration routines.
3
4Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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.
20
21 @param[in] RootPath The RootPath.
22 @param[in] Length Length of the RootPath option payload.
23 @param[in, out] ConfigData The iSCSI attempt configuration data read
24 from a nonvolatile device.
25
26 @retval EFI_SUCCESS All required information is extracted from the RootPath option.
27 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
28 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
29 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
30
31**/
32EFI_STATUS
33IScsiDhcpExtractRootPath (
34 IN CHAR8 *RootPath,
35 IN UINT8 Length,
36 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
37 )
38{
39 EFI_STATUS Status;
40 UINT8 IScsiRootPathIdLen;
41 CHAR8 *TmpStr;
42 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
43 ISCSI_ROOT_PATH_FIELD *Field;
44 UINT32 FieldIndex;
45 UINT8 Index;
46 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
47 EFI_IP_ADDRESS Ip;
48 UINT8 IpMode;
49
50 ConfigNvData = &ConfigData->SessionConfigData;
51
52 //
53 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
54 //
55 IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);
56
57 if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
58 return EFI_NOT_FOUND;
59 }
60 //
61 // Skip the iSCSI RootPath ID "iscsi:".
62 //
63 RootPath += IScsiRootPathIdLen;
64 Length = (UINT8) (Length - IScsiRootPathIdLen);
65
66 TmpStr = (CHAR8 *) AllocatePool (Length + 1);
67 if (TmpStr == NULL) {
68 return EFI_OUT_OF_RESOURCES;
69 }
70
71 CopyMem (TmpStr, RootPath, Length);
72 TmpStr[Length] = '\0';
73
74 Index = 0;
75 FieldIndex = RP_FIELD_IDX_SERVERNAME;
76 ZeroMem (&Fields[0], sizeof (Fields));
77
78 //
79 // Extract the fields in the Root Path option string.
80 //
81 for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
82 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
83 Fields[FieldIndex].Str = &TmpStr[Index];
84 }
85
86 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
87 Index++;
88 }
89
90 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
91 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
92 TmpStr[Index] = '\0';
93 Index++;
94 }
95
96 if (Fields[FieldIndex].Str != NULL) {
97 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
98 }
99 }
100 }
101
102 if (FieldIndex != RP_FIELD_IDX_MAX) {
103 Status = EFI_INVALID_PARAMETER;
104 goto ON_EXIT;
105 }
106
107 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
108 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
109 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
110 ) {
111
112 Status = EFI_INVALID_PARAMETER;
113 goto ON_EXIT;
114 }
115 //
116 // Get the IP address of the target.
117 //
118 Field = &Fields[RP_FIELD_IDX_SERVERNAME];
119
120 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
121 IpMode = ConfigNvData->IpMode;
122 } else {
123 IpMode = ConfigData->AutoConfigureMode;
124 }
125
126 //
127 // Server name is expressed as domain name, just save it.
128 //
129 if ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {
130 ConfigNvData->DnsMode = TRUE;
131 if (Field->Len > sizeof (ConfigNvData->TargetUrl)) {
132 return EFI_INVALID_PARAMETER;
133 }
134 CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
135 ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
136 } else {
137 ConfigNvData->DnsMode = FALSE;
138 ZeroMem(ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
139 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
140 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
141
142 if (EFI_ERROR (Status)) {
143 goto ON_EXIT;
144 }
145 }
146 //
147 // Check the protocol type.
148 //
149 Field = &Fields[RP_FIELD_IDX_PROTOCOL];
150 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
151 Status = EFI_INVALID_PARAMETER;
152 goto ON_EXIT;
153 }
154 //
155 // Get the port of the iSCSI target.
156 //
157 Field = &Fields[RP_FIELD_IDX_PORT];
158 if (Field->Str != NULL) {
159 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
160 } else {
161 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
162 }
163 //
164 // Get the LUN.
165 //
166 Field = &Fields[RP_FIELD_IDX_LUN];
167 if (Field->Str != NULL) {
168 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
169 if (EFI_ERROR (Status)) {
170 goto ON_EXIT;
171 }
172 } else {
173 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
174 }
175 //
176 // Get the target iSCSI Name.
177 //
178 Field = &Fields[RP_FIELD_IDX_TARGETNAME];
179
180 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
181 Status = EFI_INVALID_PARAMETER;
182 goto ON_EXIT;
183 }
184 //
185 // Validate the iSCSI name.
186 //
187 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
188 if (EFI_ERROR (Status)) {
189 goto ON_EXIT;
190 }
191
192 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
193
194ON_EXIT:
195
196 FreePool (TmpStr);
197
198 return Status;
199}
200
201/**
202 The callback function registerd to the DHCP4 instance that is used to select
203 the qualified DHCP OFFER.
204
205 @param[in] This The DHCP4 protocol.
206 @param[in] Context The context set when configuring the DHCP4 protocol.
207 @param[in] CurrentState The current state of the DHCP4 protocol.
208 @param[in] Dhcp4Event The event occurs in the current state.
209 @param[in] Packet The DHCP packet that is to be sent or was already received.
210 @param[out] NewPacket The packet used to replace the above Packet.
211
212 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
213 in the Dhcp4Event.
214 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
215 @retval Others Other errors as indicated.
216
217**/
218EFI_STATUS
219EFIAPI
220IScsiDhcpSelectOffer (
221 IN EFI_DHCP4_PROTOCOL *This,
222 IN VOID *Context,
223 IN EFI_DHCP4_STATE CurrentState,
224 IN EFI_DHCP4_EVENT Dhcp4Event,
225 IN EFI_DHCP4_PACKET *Packet, OPTIONAL
226 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
227 )
228{
229 EFI_STATUS Status;
230 UINT32 OptionCount;
231 EFI_DHCP4_PACKET_OPTION **OptionList;
232 UINT32 Index;
233
234 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
235 return EFI_SUCCESS;
236 }
237
238 OptionCount = 0;
239
240 Status = This->Parse (This, Packet, &OptionCount, NULL);
241 if (Status != EFI_BUFFER_TOO_SMALL) {
242 return EFI_NOT_READY;
243 }
244
245 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
246 if (OptionList == NULL) {
247 return EFI_NOT_READY;
248 }
249
250 Status = This->Parse (This, Packet, &OptionCount, OptionList);
251 if (EFI_ERROR (Status)) {
252 FreePool (OptionList);
253 return EFI_NOT_READY;
254 }
255
256 for (Index = 0; Index < OptionCount; Index++) {
257 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOTPATH) {
258 continue;
259 }
260
261 Status = IScsiDhcpExtractRootPath (
262 (CHAR8 *) &OptionList[Index]->Data[0],
263 OptionList[Index]->Length,
264 (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context
265 );
266
267 break;
268 }
269
270 if (Index == OptionCount) {
271 Status = EFI_NOT_READY;
272 }
273
274 FreePool (OptionList);
275
276 return Status;
277}
278
279/**
280 Parse the DHCP ACK to get the address configuration and DNS information.
281
282 @param[in] Dhcp4 The DHCP4 protocol.
283 @param[in, out] ConfigData The session configuration data.
284
285 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
286 @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.
287 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
288 @retval EFI_DEVICE_ERROR Other errors as indicated.
289 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
290
291**/
292EFI_STATUS
293IScsiParseDhcpAck (
294 IN EFI_DHCP4_PROTOCOL *Dhcp4,
295 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
296 )
297{
298 EFI_STATUS Status;
299 EFI_DHCP4_MODE_DATA Dhcp4ModeData;
300 UINT32 OptionCount;
301 EFI_DHCP4_PACKET_OPTION **OptionList;
302 UINT32 Index;
303 ISCSI_SESSION_CONFIG_NVDATA *NvData;
304
305 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
306 if (EFI_ERROR (Status)) {
307 return Status;
308 }
309
310 if (Dhcp4ModeData.State != Dhcp4Bound) {
311 return EFI_NO_MAPPING;
312 }
313
314 NvData = &ConfigData->SessionConfigData;
315
316 CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
317 CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
318 CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
319
320 OptionCount = 0;
321 OptionList = NULL;
322
323 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
324 if (Status != EFI_BUFFER_TOO_SMALL) {
325 return EFI_DEVICE_ERROR;
326 }
327
328 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
329 if (OptionList == NULL) {
330 return EFI_OUT_OF_RESOURCES;
331 }
332
333 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
334 if (EFI_ERROR (Status)) {
335 FreePool (OptionList);
336 return EFI_DEVICE_ERROR;
337 }
338
339 for (Index = 0; Index < OptionCount; Index++) {
340 //
341 // Get DNS server addresses and DHCP server address from this offer.
342 //
343 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
344
345 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
346 Status = EFI_INVALID_PARAMETER;
347 break;
348 }
349 //
350 // Primary DNS server address.
351 //
352 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
353
354 if (OptionList[Index]->Length > 4) {
355 //
356 // Secondary DNS server address.
357 //
358 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
359 }
360 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
361 if (OptionList[Index]->Length != 4) {
362 Status = EFI_INVALID_PARAMETER;
363 break;
364 }
365
366 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
367 }
368 }
369
370 FreePool (OptionList);
371
372 return Status;
373}
374
375/**
376 This function will switch the IP4 configuration policy to Static.
377
378 @param[in] Ip4Config2 Pointer to the IP4 configuration protocol.
379
380 @retval EFI_SUCCESS The policy is already configured to static.
381 @retval Others Other error as indicated.
382
383**/
384EFI_STATUS
385IScsiSetIp4Policy (
386 IN EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2
387 )
388{
389 EFI_IP4_CONFIG2_POLICY Policy;
390 EFI_STATUS Status;
391 UINTN DataSize;
392
393 DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
394 Status = Ip4Config2->GetData (
395 Ip4Config2,
396 Ip4Config2DataTypePolicy,
397 &DataSize,
398 &Policy
399 );
400 if (EFI_ERROR (Status)) {
401 return Status;
402 }
403
404 if (Policy != Ip4Config2PolicyStatic) {
405 Policy = Ip4Config2PolicyStatic;
406 Status= Ip4Config2->SetData (
407 Ip4Config2,
408 Ip4Config2DataTypePolicy,
409 sizeof (EFI_IP4_CONFIG2_POLICY),
410 &Policy
411 );
412 if (EFI_ERROR (Status)) {
413 return Status;
414 }
415 }
416
417 return EFI_SUCCESS;
418}
419
420/**
421 Parse the DHCP ACK to get the address configuration and DNS information.
422
423 @param[in] Image The handle of the driver image.
424 @param[in] Controller The handle of the controller.
425 @param[in, out] ConfigData The attempt configuration data.
426
427 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
428 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
429 @retval EFI_NO_MEDIA There was a media error.
430 @retval Others Other errors as indicated.
431
432**/
433EFI_STATUS
434IScsiDoDhcp (
435 IN EFI_HANDLE Image,
436 IN EFI_HANDLE Controller,
437 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
438 )
439{
440 EFI_HANDLE Dhcp4Handle;
441 EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
442 EFI_DHCP4_PROTOCOL *Dhcp4;
443 EFI_STATUS Status;
444 EFI_DHCP4_PACKET_OPTION *ParaList;
445 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
446 ISCSI_SESSION_CONFIG_NVDATA *NvData;
447 EFI_STATUS MediaStatus;
448
449 Dhcp4Handle = NULL;
450 Ip4Config2 = NULL;
451 Dhcp4 = NULL;
452 ParaList = NULL;
453
454 //
455 // Check media status before doing DHCP.
456 //
457 MediaStatus = EFI_SUCCESS;
458 NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus);
459 if (MediaStatus!= EFI_SUCCESS) {
460 AsciiPrint ("\n Error: Could not detect network connection.\n");
461 return EFI_NO_MEDIA;
462 }
463
464 //
465 // DHCP4 service allows only one of its children to be configured in
466 // the active state, If the DHCP4 D.O.R.A started by IP4 auto
467 // configuration and has not been completed, the Dhcp4 state machine
468 // will not be in the right state for the iSCSI to start a new round D.O.R.A.
469 // So, we need to switch it's policy to static.
470 //
471 Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);
472 if (!EFI_ERROR (Status)) {
473 Status = IScsiSetIp4Policy (Ip4Config2);
474 if (EFI_ERROR (Status)) {
475 return Status;
476 }
477 }
478
479 //
480 // Create a DHCP4 child instance and get the protocol.
481 //
482 Status = NetLibCreateServiceChild (
483 Controller,
484 Image,
485 &gEfiDhcp4ServiceBindingProtocolGuid,
486 &Dhcp4Handle
487 );
488 if (EFI_ERROR (Status)) {
489 return Status;
490 }
491
492 Status = gBS->OpenProtocol (
493 Dhcp4Handle,
494 &gEfiDhcp4ProtocolGuid,
495 (VOID **) &Dhcp4,
496 Image,
497 Controller,
498 EFI_OPEN_PROTOCOL_BY_DRIVER
499 );
500 if (EFI_ERROR (Status)) {
501 goto ON_EXIT;
502 }
503
504 NvData = &ConfigData->SessionConfigData;
505
506 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
507 if (ParaList == NULL) {
508 Status = EFI_OUT_OF_RESOURCES;
509 goto ON_EXIT;
510 }
511
512 //
513 // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
514 //
515 ParaList->OpCode = DHCP4_TAG_PARA_LIST;
516 ParaList->Length = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3);
517 ParaList->Data[0] = DHCP4_TAG_NETMASK;
518 ParaList->Data[1] = DHCP4_TAG_ROUTER;
519 ParaList->Data[2] = DHCP4_TAG_DNS_SERVER;
520 ParaList->Data[3] = DHCP4_TAG_ROOTPATH;
521
522 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
523 Dhcp4ConfigData.OptionCount = 1;
524 Dhcp4ConfigData.OptionList = &ParaList;
525
526 if (NvData->TargetInfoFromDhcp) {
527 //
528 // Use callback to select an offer that contains target information.
529 //
530 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
531 Dhcp4ConfigData.CallbackContext = ConfigData;
532 }
533
534 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
535 if (EFI_ERROR (Status)) {
536 goto ON_EXIT;
537 }
538
539 Status = Dhcp4->Start (Dhcp4, NULL);
540 if (EFI_ERROR (Status)) {
541 goto ON_EXIT;
542 }
543 //
544 // Parse the ACK to get required information.
545 //
546 Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
547
548ON_EXIT:
549
550 if (ParaList != NULL) {
551 FreePool (ParaList);
552 }
553
554 if (Dhcp4 != NULL) {
555 Dhcp4->Stop (Dhcp4);
556 Dhcp4->Configure (Dhcp4, NULL);
557
558 gBS->CloseProtocol (
559 Dhcp4Handle,
560 &gEfiDhcp4ProtocolGuid,
561 Image,
562 Controller
563 );
564 }
565
566 NetLibDestroyServiceChild (
567 Controller,
568 Image,
569 &gEfiDhcp4ServiceBindingProtocolGuid,
570 Dhcp4Handle
571 );
572
573 return Status;
574}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette