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