]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c
Use Mde library and definition instead of some native definitions in NetLib, to simpl...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / IScsiDxe / IScsiDhcp.c
1 /*++
2
3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 IScsiDhcp.c
15
16 Abstract:
17
18 iSCSI DHCP related configuration routines.
19
20 --*/
21
22 #include "IScsiImpl.h"
23
24 STATIC
25 EFI_STATUS
26 IScsiDhcpExtractRootPath (
27 IN CHAR8 *RootPath,
28 IN UINT8 Length,
29 IN ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData
30 )
31 /*++
32
33 Routine Description:
34
35 Extract the Root Path option and get the required target information.
36
37 Arguments:
38
39 RootPath - The RootPath.
40 Length - Length of the RootPath option payload.
41 ConfigNvData - The iSCSI session configuration data read from nonvolatile device.
42
43 Returns:
44
45 EFI_SUCCESS - All required information is extracted from the RootPath option.
46 EFI_NOT_FOUND - The RootPath is not an iSCSI RootPath.
47 EFI_OUT_OF_RESOURCES - Failed to allocate memory.
48 EFI_INVALID_PARAMETER - The RootPath is mal-formatted.
49
50 --*/
51 {
52 EFI_STATUS Status;
53 UINT8 IScsiRootPathIdLen;
54 CHAR8 *TmpStr;
55 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
56 ISCSI_ROOT_PATH_FIELD *Field;
57 UINT32 FieldIndex;
58 UINT8 Index;
59
60 //
61 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
62 //
63 IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);
64
65 if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
66 return EFI_NOT_FOUND;
67 }
68 //
69 // Skip the iSCSI RootPath ID "iscsi:".
70 //
71 RootPath += IScsiRootPathIdLen;
72 Length = (UINT8) (Length - IScsiRootPathIdLen);
73
74 TmpStr = (CHAR8 *) AllocatePool (Length + 1);
75 if (TmpStr == NULL) {
76 return EFI_OUT_OF_RESOURCES;
77 }
78
79 CopyMem (TmpStr, RootPath, Length);
80 TmpStr[Length] = '\0';
81
82 Index = 0;
83 FieldIndex = 0;
84 ZeroMem (&Fields[0], sizeof (Fields));
85
86 //
87 // Extract the fields in the Root Path option string.
88 //
89 for (FieldIndex = 0; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
90 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
91 Fields[FieldIndex].Str = &TmpStr[Index];
92 }
93
94 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
95 Index++;
96 }
97
98 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
99 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
100 TmpStr[Index] = '\0';
101 Index++;
102 }
103
104 if (Fields[FieldIndex].Str != NULL) {
105 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
106 }
107 }
108 }
109
110 if (FieldIndex != RP_FIELD_IDX_MAX) {
111 Status = EFI_INVALID_PARAMETER;
112 goto ON_EXIT;
113 }
114
115 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
116 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
117 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
118 ) {
119
120 Status = EFI_INVALID_PARAMETER;
121 goto ON_EXIT;
122 }
123 //
124 // Get the IP address of the target.
125 //
126 Field = &Fields[RP_FIELD_IDX_SERVERNAME];
127 Status = IScsiAsciiStrToIp (Field->Str, &ConfigNvData->TargetIp);
128 if (EFI_ERROR (Status)) {
129 goto ON_EXIT;
130 }
131 //
132 // Check the protocol type.
133 //
134 Field = &Fields[RP_FIELD_IDX_PROTOCOL];
135 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
136 Status = EFI_INVALID_PARAMETER;
137 goto ON_EXIT;
138 }
139 //
140 // Get the port of the iSCSI target.
141 //
142 Field = &Fields[RP_FIELD_IDX_PORT];
143 if (Field->Str != NULL) {
144 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
145 } else {
146 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
147 }
148 //
149 // Get the LUN.
150 //
151 Field = &Fields[RP_FIELD_IDX_LUN];
152 if (Field->Str != NULL) {
153 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
154 if (EFI_ERROR (Status)) {
155 goto ON_EXIT;
156 }
157 } else {
158 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
159 }
160 //
161 // Get the target iSCSI Name.
162 //
163 Field = &Fields[RP_FIELD_IDX_TARGETNAME];
164
165 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
166 Status = EFI_INVALID_PARAMETER;
167 goto ON_EXIT;
168 }
169 //
170 // Validate the iSCSI name.
171 //
172 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
173 if (EFI_ERROR (Status)) {
174 goto ON_EXIT;
175 }
176
177 AsciiStrCpy (ConfigNvData->TargetName, Field->Str);
178
179 ON_EXIT:
180
181 gBS->FreePool (TmpStr);
182
183 return Status;
184 }
185
186 STATIC
187 EFI_STATUS
188 IScsiDhcpSelectOffer (
189 IN EFI_DHCP4_PROTOCOL * This,
190 IN VOID *Context,
191 IN EFI_DHCP4_STATE CurrentState,
192 IN EFI_DHCP4_EVENT Dhcp4Event,
193 IN EFI_DHCP4_PACKET * Packet, OPTIONAL
194 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
195 )
196 /*++
197
198 Routine Description:
199
200 The callback function registerd to the DHCP4 instance which is used to select
201 the qualified DHCP OFFER.
202
203 Arguments:
204
205 This - The DHCP4 protocol.
206 Context - The context set when configuring the DHCP4 protocol.
207 CurrentState - The current state of the DHCP4 protocol.
208 Dhcp4Event - The event occurs in the current state.
209 Packet - The DHCP packet that is to be sent or already received.
210 NewPackt - The packet used to replace the above Packet.
211
212 Returns:
213
214 EFI_NOT_READY - The DHCP OFFER packet doesn't match our requirements.
215 EFI_SUCCESS - Either the DHCP OFFER is qualified or we're not intereseted
216 in the Dhcp4Event.
217
218 --*/
219 {
220 EFI_STATUS Status;
221 UINT32 OptionCount;
222 EFI_DHCP4_PACKET_OPTION **OptionList;
223 UINT32 Index;
224
225 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
226 return EFI_SUCCESS;
227 }
228
229 OptionCount = 0;
230
231 Status = This->Parse (This, Packet, &OptionCount, NULL);
232 if (Status != EFI_BUFFER_TOO_SMALL) {
233 return EFI_NOT_READY;
234 }
235
236 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
237 if (OptionList == NULL) {
238 return EFI_NOT_READY;
239 }
240
241 Status = This->Parse (This, Packet, &OptionCount, OptionList);
242 if (EFI_ERROR (Status)) {
243 gBS->FreePool (OptionList);
244 return EFI_NOT_READY;
245 }
246
247 for (Index = 0; Index < OptionCount; Index++) {
248 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {
249 continue;
250 }
251
252 Status = IScsiDhcpExtractRootPath (
253 (CHAR8 *) &OptionList[Index]->Data[0],
254 OptionList[Index]->Length,
255 (ISCSI_SESSION_CONFIG_NVDATA *) Context
256 );
257
258 break;
259 }
260
261 if ((Index == OptionCount)) {
262 Status = EFI_NOT_READY;
263 }
264
265 gBS->FreePool (OptionList);
266
267 return Status;
268 }
269
270 EFI_STATUS
271 IScsiParseDhcpAck (
272 IN EFI_DHCP4_PROTOCOL *Dhcp4,
273 IN ISCSI_SESSION_CONFIG_DATA *ConfigData
274 )
275 /*++
276
277 Routine Description:
278
279 Parse the DHCP ACK to get the address configuration and DNS information.
280
281 Arguments:
282
283 Dhcp4 - The DHCP4 protocol.
284 ConfigData - The session configuration data.
285
286 Returns:
287
288 EFI_SUCCESS - The DNS information is got from the DHCP ACK.
289 EFI_NO_MAPPING - DHCP failed to acquire address and other information.
290 EFI_INVALID_PARAMETER - The DHCP ACK's DNS option is mal-formatted.
291 EFI_DEVICE_ERROR - Some unexpected error happened.
292
293 --*/
294 {
295 EFI_STATUS Status;
296 EFI_DHCP4_MODE_DATA Dhcp4ModeData;
297 UINT32 OptionCount;
298 EFI_DHCP4_PACKET_OPTION **OptionList;
299 UINT32 Index;
300
301 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
302 if (EFI_ERROR (Status)) {
303 return Status;
304 }
305
306 if (Dhcp4ModeData.State != Dhcp4Bound) {
307 return EFI_NO_MAPPING;
308 }
309
310 CopyMem (&ConfigData->NvData.LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
311 CopyMem (&ConfigData->NvData.SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
312 CopyMem (&ConfigData->NvData.Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
313
314 OptionCount = 0;
315 OptionList = NULL;
316
317 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
318 if (Status != EFI_BUFFER_TOO_SMALL) {
319 return EFI_DEVICE_ERROR;
320 }
321
322 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
323 if (OptionList == NULL) {
324 return EFI_OUT_OF_RESOURCES;
325 }
326
327 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
328 if (EFI_ERROR (Status)) {
329 gBS->FreePool (OptionList);
330 return EFI_DEVICE_ERROR;
331 }
332
333 for (Index = 0; Index < OptionCount; Index++) {
334 //
335 // Get DNS server addresses and DHCP server address from this offer.
336 //
337 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {
338
339 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
340 Status = EFI_INVALID_PARAMETER;
341 break;
342 }
343 //
344 // Primary DNS server address.
345 //
346 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
347
348 if (OptionList[Index]->Length > 4) {
349 //
350 // Secondary DNS server address
351 //
352 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
353 }
354 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
355 if (OptionList[Index]->Length != 4) {
356 Status = EFI_INVALID_PARAMETER;
357 break;
358 }
359
360 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
361 }
362 }
363
364 gBS->FreePool (OptionList);
365
366 return Status;
367 }
368
369 EFI_STATUS
370 IScsiDoDhcp (
371 IN EFI_HANDLE Image,
372 IN EFI_HANDLE Controller,
373 IN ISCSI_SESSION_CONFIG_DATA *ConfigData
374 )
375 /*++
376
377 Routine Description:
378
379 Parse the DHCP ACK to get the address configuration and DNS information.
380
381 Arguments:
382
383 Image - The handle of the driver image.
384 Controller - The handle of the controller;
385 ConfigData - The session configuration data.
386
387 Returns:
388
389 EFI_SUCCESS - The DNS information is got from the DHCP ACK.
390 EFI_NO_MAPPING - DHCP failed to acquire address and other information.
391 EFI_INVALID_PARAMETER - The DHCP ACK's DNS option is mal-formatted.
392 EFI_DEVICE_ERROR - Some unexpected error happened.
393
394 --*/
395 {
396 EFI_HANDLE Dhcp4Handle;
397 EFI_DHCP4_PROTOCOL *Dhcp4;
398 EFI_STATUS Status;
399 EFI_DHCP4_PACKET_OPTION *ParaList;
400 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
401
402 Dhcp4Handle = NULL;
403 Dhcp4 = NULL;
404 ParaList = NULL;
405
406 //
407 // Create a DHCP4 child instance and get the protocol.
408 //
409 Status = NetLibCreateServiceChild (
410 Controller,
411 Image,
412 &gEfiDhcp4ServiceBindingProtocolGuid,
413 &Dhcp4Handle
414 );
415 if (EFI_ERROR (Status)) {
416 return Status;
417 }
418
419 Status = gBS->OpenProtocol (
420 Dhcp4Handle,
421 &gEfiDhcp4ProtocolGuid,
422 (VOID **)&Dhcp4,
423 Image,
424 Controller,
425 EFI_OPEN_PROTOCOL_BY_DRIVER
426 );
427 if (EFI_ERROR (Status)) {
428 goto ON_EXIT;
429 }
430
431 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
432 if (ParaList == NULL) {
433 Status = EFI_OUT_OF_RESOURCES;
434 goto ON_EXIT;
435 }
436 //
437 // Ask the server to reply with Netmask, Router, DNS and RootPath options.
438 //
439 ParaList->OpCode = DHCP4_TAG_PARA_LIST;
440 ParaList->Length = (UINT8) (ConfigData->NvData.TargetInfoFromDhcp ? 4 : 3);
441 ParaList->Data[0] = DHCP4_TAG_NETMASK;
442 ParaList->Data[1] = DHCP4_TAG_ROUTER;
443 ParaList->Data[2] = DHCP4_TAG_DNS;
444 ParaList->Data[3] = DHCP4_TAG_ROOT_PATH;
445
446 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
447 Dhcp4ConfigData.OptionCount = 1;
448 Dhcp4ConfigData.OptionList = &ParaList;
449
450 if (ConfigData->NvData.TargetInfoFromDhcp) {
451 //
452 // Use callback to select an offer which contains target information.
453 //
454 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
455 Dhcp4ConfigData.CallbackContext = &ConfigData->NvData;
456 }
457
458 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
459 if (EFI_ERROR (Status)) {
460 goto ON_EXIT;
461 }
462
463 Status = Dhcp4->Start (Dhcp4, NULL);
464 if (EFI_ERROR (Status)) {
465 goto ON_EXIT;
466 }
467 //
468 // Parse the ACK to get required information.
469 //
470 Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
471
472 ON_EXIT:
473
474 if (ParaList != NULL) {
475 gBS->FreePool (ParaList);
476 }
477
478 if (Dhcp4 != NULL) {
479 Dhcp4->Stop (Dhcp4);
480 Dhcp4->Configure (Dhcp4, NULL);
481
482 gBS->CloseProtocol (
483 Dhcp4Handle,
484 &gEfiDhcp4ProtocolGuid,
485 Image,
486 Controller
487 );
488 }
489
490 NetLibDestroyServiceChild (
491 Controller,
492 Image,
493 &gEfiDhcp4ServiceBindingProtocolGuid,
494 Dhcp4Handle
495 );
496
497 return Status;
498 }