]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiDhcp6.c
NetworkPkg:Replace unsafe string functions.
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp6.c
1 /** @file
2 iSCSI DHCP6 related configuration routines.
3
4 Copyright (c) 2009 - 2015, 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 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, 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 UINT16 ParaLen;
254
255 OptionCount = 0;
256 BootFileOpt = NULL;
257
258 Status = This->Parse (This, Packet, &OptionCount, NULL);
259 if (Status != EFI_BUFFER_TOO_SMALL) {
260 return EFI_NOT_READY;
261 }
262
263 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
264 if (OptionList == NULL) {
265 return EFI_NOT_READY;
266 }
267
268 Status = This->Parse (This, Packet, &OptionCount, OptionList);
269 if (EFI_ERROR (Status)) {
270 Status = EFI_NOT_READY;
271 goto Exit;
272 }
273
274 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;
275
276 for (Index = 0; Index < OptionCount; Index++) {
277 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
278 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);
279
280 //
281 // Get DNS server addresses from this reply packet.
282 //
283 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {
284
285 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
286 Status = EFI_UNSUPPORTED;
287 goto Exit;
288 }
289 //
290 // Primary DNS server address.
291 //
292 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));
293
294 if (OptionList[Index]->OpLen > 16) {
295 //
296 // Secondary DNS server address
297 //
298 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));
299 }
300
301 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {
302 //
303 // The server sends this option to inform the client about an URL to a boot file.
304 //
305 BootFileOpt = OptionList[Index];
306 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARA) {
307 //
308 // The server sends this option to inform the client about DHCP6 server address.
309 //
310 if (OptionList[Index]->OpLen < 18) {
311 Status = EFI_UNSUPPORTED;
312 goto Exit;
313 }
314 //
315 // Check param-len 1, should be 16 bytes.
316 //
317 CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16));
318 if (NTOHS (ParaLen) != 16) {
319 Status = EFI_UNSUPPORTED;
320 goto Exit;
321 }
322
323 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS));
324 }
325 }
326
327 if (BootFileOpt == NULL) {
328 Status = EFI_UNSUPPORTED;
329 goto Exit;
330 }
331
332 //
333 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
334 //
335 Status = IScsiDhcp6ExtractRootPath (
336 (CHAR8 *) BootFileOpt->Data,
337 BootFileOpt->OpLen,
338 ConfigData
339 );
340
341 Exit:
342
343 FreePool (OptionList);
344 return Status;
345 }
346
347
348 /**
349 Parse the DHCP ACK to get the address configuration and DNS information.
350
351 @param[in] Image The handle of the driver image.
352 @param[in] Controller The handle of the controller;
353 @param[in, out] ConfigData The attempt configuration data.
354
355 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
356 @retval EFI_NO_MAPPING DHCP failed to acquire address and other
357 information.
358 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
359 @retval EFI_DEVICE_ERROR Some unexpected error occurred.
360 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
361 operation.
362 @retval EFI_NO_MEDIA There was a media error.
363
364 **/
365 EFI_STATUS
366 IScsiDoDhcp6 (
367 IN EFI_HANDLE Image,
368 IN EFI_HANDLE Controller,
369 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
370 )
371 {
372 EFI_HANDLE Dhcp6Handle;
373 EFI_DHCP6_PROTOCOL *Dhcp6;
374 EFI_STATUS Status;
375 EFI_STATUS TimerStatus;
376 EFI_DHCP6_PACKET_OPTION *Oro;
377 EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
378 EFI_EVENT Timer;
379 BOOLEAN MediaPresent;
380
381 //
382 // Check media status before doing DHCP.
383 //
384 MediaPresent = TRUE;
385 NetLibDetectMedia (Controller, &MediaPresent);
386 if (!MediaPresent) {
387 return EFI_NO_MEDIA;
388 }
389
390 //
391 // iSCSI will only request target info from DHCPv6 server.
392 //
393 if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {
394 return EFI_SUCCESS;
395 }
396
397 Dhcp6Handle = NULL;
398 Dhcp6 = NULL;
399 Oro = NULL;
400 Timer = NULL;
401
402 //
403 // Create a DHCP6 child instance and get the protocol.
404 //
405 Status = NetLibCreateServiceChild (
406 Controller,
407 Image,
408 &gEfiDhcp6ServiceBindingProtocolGuid,
409 &Dhcp6Handle
410 );
411 if (EFI_ERROR (Status)) {
412 return Status;
413 }
414
415 Status = gBS->OpenProtocol (
416 Dhcp6Handle,
417 &gEfiDhcp6ProtocolGuid,
418 (VOID **) &Dhcp6,
419 Image,
420 Controller,
421 EFI_OPEN_PROTOCOL_BY_DRIVER
422 );
423 if (EFI_ERROR (Status)) {
424 goto ON_EXIT;
425 }
426
427 Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5);
428 if (Oro == NULL) {
429 Status = EFI_OUT_OF_RESOURCES;
430 goto ON_EXIT;
431 }
432
433 //
434 // Ask the server to reply with DNS and Boot File URL options by info request.
435 // All members in EFI_DHCP6_PACKET_OPTION are in network order.
436 //
437 Oro->OpCode = HTONS (DHCP6_OPT_REQUEST_OPTION);
438 Oro->OpLen = HTONS (2 * 3);
439 Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;
440 Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;
441 Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARA;
442
443 InfoReqReXmit.Irt = 4;
444 InfoReqReXmit.Mrc = 1;
445 InfoReqReXmit.Mrt = 10;
446 InfoReqReXmit.Mrd = 30;
447
448 Status = Dhcp6->InfoRequest (
449 Dhcp6,
450 TRUE,
451 Oro,
452 0,
453 NULL,
454 &InfoReqReXmit,
455 NULL,
456 IScsiDhcp6ParseReply,
457 ConfigData
458 );
459 if (Status == EFI_NO_MAPPING) {
460 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
461 if (EFI_ERROR (Status)) {
462 goto ON_EXIT;
463 }
464
465 Status = gBS->SetTimer (
466 Timer,
467 TimerRelative,
468 ISCSI_GET_MAPPING_TIMEOUT
469 );
470
471 if (EFI_ERROR (Status)) {
472 goto ON_EXIT;
473 }
474
475 do {
476
477 TimerStatus = gBS->CheckEvent (Timer);
478
479 if (!EFI_ERROR (TimerStatus)) {
480 Status = Dhcp6->InfoRequest (
481 Dhcp6,
482 TRUE,
483 Oro,
484 0,
485 NULL,
486 &InfoReqReXmit,
487 NULL,
488 IScsiDhcp6ParseReply,
489 ConfigData
490 );
491 }
492
493 } while (TimerStatus == EFI_NOT_READY);
494
495 }
496
497 ON_EXIT:
498
499 if (Oro != NULL) {
500 FreePool (Oro);
501 }
502
503 if (Timer != NULL) {
504 gBS->CloseEvent (Timer);
505 }
506
507 if (Dhcp6 != NULL) {
508 gBS->CloseProtocol (
509 Dhcp6Handle,
510 &gEfiDhcp6ProtocolGuid,
511 Image,
512 Controller
513 );
514 }
515
516 NetLibDestroyServiceChild (
517 Controller,
518 Image,
519 &gEfiDhcp6ServiceBindingProtocolGuid,
520 Dhcp6Handle
521 );
522
523 return Status;
524 }
525