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