• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   iSCSI DHCP4 related configuration routines.
3 
4 Copyright (c) 2004 - 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.
20 
21   @param[in]        RootPath         The RootPath.
22   @param[in]        Length           Length of the RootPath option payload.
23   @param[in, out]   ConfigData       The iSCSI attempt configuration data read
24                                      from a nonvolatile device.
25 
26   @retval EFI_SUCCESS           All required information is extracted from the RootPath option.
27   @retval EFI_NOT_FOUND         The RootPath is not an iSCSI RootPath.
28   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
29   @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
30 
31 **/
32 EFI_STATUS
IScsiDhcpExtractRootPath(IN CHAR8 * RootPath,IN UINT8 Length,IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA * ConfigData)33 IScsiDhcpExtractRootPath (
34   IN      CHAR8                        *RootPath,
35   IN      UINT8                        Length,
36   IN OUT  ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
37   )
38 {
39   EFI_STATUS                  Status;
40   UINT8                       IScsiRootPathIdLen;
41   CHAR8                       *TmpStr;
42   ISCSI_ROOT_PATH_FIELD       Fields[RP_FIELD_IDX_MAX];
43   ISCSI_ROOT_PATH_FIELD       *Field;
44   UINT32                      FieldIndex;
45   UINT8                       Index;
46   ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
47   EFI_IP_ADDRESS              Ip;
48   UINT8                       IpMode;
49 
50   ConfigNvData = &ConfigData->SessionConfigData;
51 
52   //
53   // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
54   //
55   IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);
56 
57   if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
58     return EFI_NOT_FOUND;
59   }
60   //
61   // Skip the iSCSI RootPath ID "iscsi:".
62   //
63   RootPath += IScsiRootPathIdLen;
64   Length  = (UINT8) (Length - IScsiRootPathIdLen);
65 
66   TmpStr  = (CHAR8 *) AllocatePool (Length + 1);
67   if (TmpStr == NULL) {
68     return EFI_OUT_OF_RESOURCES;
69   }
70 
71   CopyMem (TmpStr, RootPath, Length);
72   TmpStr[Length]  = '\0';
73 
74   Index           = 0;
75   FieldIndex      = RP_FIELD_IDX_SERVERNAME;
76   ZeroMem (&Fields[0], sizeof (Fields));
77 
78   //
79   // Extract the fields in the Root Path option string.
80   //
81   for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
82     if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
83       Fields[FieldIndex].Str = &TmpStr[Index];
84     }
85 
86     while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
87       Index++;
88     }
89 
90     if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
91       if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
92         TmpStr[Index] = '\0';
93         Index++;
94       }
95 
96       if (Fields[FieldIndex].Str != NULL) {
97         Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
98       }
99     }
100   }
101 
102   if (FieldIndex != RP_FIELD_IDX_MAX) {
103     Status = EFI_INVALID_PARAMETER;
104     goto ON_EXIT;
105   }
106 
107   if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
108       (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
109       (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
110       ) {
111 
112     Status = EFI_INVALID_PARAMETER;
113     goto ON_EXIT;
114   }
115   //
116   // Get the IP address of the target.
117   //
118   Field   = &Fields[RP_FIELD_IDX_SERVERNAME];
119 
120   if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
121     IpMode = ConfigNvData->IpMode;
122   } else {
123     IpMode = ConfigData->AutoConfigureMode;
124   }
125 
126   Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
127   CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
128 
129   if (EFI_ERROR (Status)) {
130     goto ON_EXIT;
131   }
132   //
133   // Check the protocol type.
134   //
135   Field = &Fields[RP_FIELD_IDX_PROTOCOL];
136   if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
137     Status = EFI_INVALID_PARAMETER;
138     goto ON_EXIT;
139   }
140   //
141   // Get the port of the iSCSI target.
142   //
143   Field = &Fields[RP_FIELD_IDX_PORT];
144   if (Field->Str != NULL) {
145     ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
146   } else {
147     ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
148   }
149   //
150   // Get the LUN.
151   //
152   Field = &Fields[RP_FIELD_IDX_LUN];
153   if (Field->Str != NULL) {
154     Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
155     if (EFI_ERROR (Status)) {
156       goto ON_EXIT;
157     }
158   } else {
159     ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
160   }
161   //
162   // Get the target iSCSI Name.
163   //
164   Field = &Fields[RP_FIELD_IDX_TARGETNAME];
165 
166   if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
167     Status = EFI_INVALID_PARAMETER;
168     goto ON_EXIT;
169   }
170   //
171   // Validate the iSCSI name.
172   //
173   Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
174   if (EFI_ERROR (Status)) {
175     goto ON_EXIT;
176   }
177 
178   AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
179 
180 ON_EXIT:
181 
182   FreePool (TmpStr);
183 
184   return Status;
185 }
186 
187 /**
188   The callback function registerd to the DHCP4 instance that is used to select
189   the qualified DHCP OFFER.
190 
191   @param[in]  This         The DHCP4 protocol.
192   @param[in]  Context      The context set when configuring the DHCP4 protocol.
193   @param[in]  CurrentState The current state of the DHCP4 protocol.
194   @param[in]  Dhcp4Event   The event occurs in the current state.
195   @param[in]  Packet       The DHCP packet that is to be sent or was already received.
196   @param[out] NewPacket    The packet used to replace the above Packet.
197 
198   @retval EFI_SUCCESS      Either the DHCP OFFER is qualified or we're not intereseted
199                            in the Dhcp4Event.
200   @retval EFI_NOT_READY    The DHCP OFFER packet doesn't match our requirements.
201   @retval Others           Other errors as indicated.
202 
203 **/
204 EFI_STATUS
205 EFIAPI
IScsiDhcpSelectOffer(IN EFI_DHCP4_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP4_STATE CurrentState,IN EFI_DHCP4_EVENT Dhcp4Event,IN EFI_DHCP4_PACKET * Packet,OPTIONAL OUT EFI_DHCP4_PACKET ** NewPacket OPTIONAL)206 IScsiDhcpSelectOffer (
207   IN  EFI_DHCP4_PROTOCOL  *This,
208   IN  VOID                *Context,
209   IN  EFI_DHCP4_STATE     CurrentState,
210   IN  EFI_DHCP4_EVENT     Dhcp4Event,
211   IN  EFI_DHCP4_PACKET    *Packet, OPTIONAL
212   OUT EFI_DHCP4_PACKET    **NewPacket OPTIONAL
213   )
214 {
215   EFI_STATUS              Status;
216   UINT32                  OptionCount;
217   EFI_DHCP4_PACKET_OPTION **OptionList;
218   UINT32                  Index;
219 
220   if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
221     return EFI_SUCCESS;
222   }
223 
224   OptionCount = 0;
225 
226   Status      = This->Parse (This, Packet, &OptionCount, NULL);
227   if (Status != EFI_BUFFER_TOO_SMALL) {
228     return EFI_NOT_READY;
229   }
230 
231   OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
232   if (OptionList == NULL) {
233     return EFI_NOT_READY;
234   }
235 
236   Status = This->Parse (This, Packet, &OptionCount, OptionList);
237   if (EFI_ERROR (Status)) {
238     FreePool (OptionList);
239     return EFI_NOT_READY;
240   }
241 
242   for (Index = 0; Index < OptionCount; Index++) {
243     if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {
244       continue;
245     }
246 
247     Status = IScsiDhcpExtractRootPath (
248                (CHAR8 *) &OptionList[Index]->Data[0],
249                OptionList[Index]->Length,
250                (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context
251                );
252 
253     break;
254   }
255 
256   if ((Index == OptionCount)) {
257     Status = EFI_NOT_READY;
258   }
259 
260   FreePool (OptionList);
261 
262   return Status;
263 }
264 
265 /**
266   Parse the DHCP ACK to get the address configuration and DNS information.
267 
268   @param[in]       Dhcp4        The DHCP4 protocol.
269   @param[in, out]  ConfigData   The session configuration data.
270 
271   @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.
272   @retval EFI_NO_MAPPING        DHCP failed to acquire address and other information.
273   @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
274   @retval EFI_DEVICE_ERROR      Other errors as indicated.
275   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
276 
277 **/
278 EFI_STATUS
IScsiParseDhcpAck(IN EFI_DHCP4_PROTOCOL * Dhcp4,IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA * ConfigData)279 IScsiParseDhcpAck (
280   IN     EFI_DHCP4_PROTOCOL          *Dhcp4,
281   IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
282   )
283 {
284   EFI_STATUS                    Status;
285   EFI_DHCP4_MODE_DATA           Dhcp4ModeData;
286   UINT32                        OptionCount;
287   EFI_DHCP4_PACKET_OPTION       **OptionList;
288   UINT32                        Index;
289   ISCSI_SESSION_CONFIG_NVDATA   *NvData;
290 
291   Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
292   if (EFI_ERROR (Status)) {
293     return Status;
294   }
295 
296   if (Dhcp4ModeData.State != Dhcp4Bound) {
297     return EFI_NO_MAPPING;
298   }
299 
300   NvData = &ConfigData->SessionConfigData;
301 
302   CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
303   CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
304   CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
305 
306   OptionCount = 0;
307   OptionList  = NULL;
308 
309   Status      = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
310   if (Status != EFI_BUFFER_TOO_SMALL) {
311     return EFI_DEVICE_ERROR;
312   }
313 
314   OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
315   if (OptionList == NULL) {
316     return EFI_OUT_OF_RESOURCES;
317   }
318 
319   Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
320   if (EFI_ERROR (Status)) {
321     FreePool (OptionList);
322     return EFI_DEVICE_ERROR;
323   }
324 
325   for (Index = 0; Index < OptionCount; Index++) {
326     //
327     // Get DNS server addresses and DHCP server address from this offer.
328     //
329     if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {
330 
331       if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
332         Status = EFI_INVALID_PARAMETER;
333         break;
334       }
335       //
336       // Primary DNS server address.
337       //
338       CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
339 
340       if (OptionList[Index]->Length > 4) {
341         //
342         // Secondary DNS server address.
343         //
344         CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
345       }
346     } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
347       if (OptionList[Index]->Length != 4) {
348         Status = EFI_INVALID_PARAMETER;
349         break;
350       }
351 
352       CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
353     }
354   }
355 
356   FreePool (OptionList);
357 
358   return Status;
359 }
360 
361 
362 /**
363   Parse the DHCP ACK to get the address configuration and DNS information.
364 
365   @param[in]       Image            The handle of the driver image.
366   @param[in]       Controller       The handle of the controller.
367   @param[in, out]  ConfigData       The attempt configuration data.
368 
369   @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.
370   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
371   @retval EFI_NO_MEDIA          There was a media error.
372   @retval Others                Other errors as indicated.
373 
374 **/
375 EFI_STATUS
IScsiDoDhcp(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA * ConfigData)376 IScsiDoDhcp (
377   IN     EFI_HANDLE                  Image,
378   IN     EFI_HANDLE                  Controller,
379   IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
380   )
381 {
382   EFI_HANDLE                    Dhcp4Handle;
383   EFI_DHCP4_PROTOCOL            *Dhcp4;
384   EFI_STATUS                    Status;
385   EFI_DHCP4_PACKET_OPTION       *ParaList;
386   EFI_DHCP4_CONFIG_DATA         Dhcp4ConfigData;
387   ISCSI_SESSION_CONFIG_NVDATA   *NvData;
388   BOOLEAN                       MediaPresent;
389 
390   Dhcp4Handle = NULL;
391   Dhcp4       = NULL;
392   ParaList    = NULL;
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   // Create a DHCP4 child instance and get the protocol.
405   //
406   Status = NetLibCreateServiceChild (
407              Controller,
408              Image,
409              &gEfiDhcp4ServiceBindingProtocolGuid,
410              &Dhcp4Handle
411              );
412   if (EFI_ERROR (Status)) {
413     return Status;
414   }
415 
416   Status = gBS->OpenProtocol (
417                   Dhcp4Handle,
418                   &gEfiDhcp4ProtocolGuid,
419                   (VOID **) &Dhcp4,
420                   Image,
421                   Controller,
422                   EFI_OPEN_PROTOCOL_BY_DRIVER
423                   );
424   if (EFI_ERROR (Status)) {
425     goto ON_EXIT;
426   }
427 
428   NvData   = &ConfigData->SessionConfigData;
429 
430   ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
431   if (ParaList == NULL) {
432     Status = EFI_OUT_OF_RESOURCES;
433     goto ON_EXIT;
434   }
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) (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 (NvData->TargetInfoFromDhcp) {
451     //
452     // Use callback to select an offer that contains target information.
453     //
454     Dhcp4ConfigData.Dhcp4Callback   = IScsiDhcpSelectOffer;
455     Dhcp4ConfigData.CallbackContext = ConfigData;
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     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 }
499