]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiDhcp.c
NetworkPkg: Clean up source files
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp.c
1 /** @file
2 iSCSI DHCP4 related configuration routines.
3
4 Copyright (c) 2004 - 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.
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 **/
32 EFI_STATUS
33 IScsiDhcpExtractRootPath (
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
194 ON_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 **/
218 EFI_STATUS
219 EFIAPI
220 IScsiDhcpSelectOffer (
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 **/
292 EFI_STATUS
293 IScsiParseDhcpAck (
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 **/
384 EFI_STATUS
385 IScsiSetIp4Policy (
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 **/
433 EFI_STATUS
434 IScsiDoDhcp (
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
548 ON_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 }