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