IScsiDhcp.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /** @file
  2. iSCSI DHCP4 related configuration routines.
  3. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "IScsiImpl.h"
  7. /**
  8. Extract the Root Path option and get the required target information.
  9. @param[in] RootPath The RootPath.
  10. @param[in] Length Length of the RootPath option payload.
  11. @param[in, out] ConfigData The iSCSI attempt configuration data read
  12. from a nonvolatile device.
  13. @retval EFI_SUCCESS All required information is extracted from the RootPath option.
  14. @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
  15. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  16. @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
  17. **/
  18. EFI_STATUS
  19. IScsiDhcpExtractRootPath (
  20. IN CHAR8 *RootPath,
  21. IN UINT8 Length,
  22. IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
  23. )
  24. {
  25. EFI_STATUS Status;
  26. UINT8 IScsiRootPathIdLen;
  27. CHAR8 *TmpStr;
  28. ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
  29. ISCSI_ROOT_PATH_FIELD *Field;
  30. UINT32 FieldIndex;
  31. UINT8 Index;
  32. ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
  33. EFI_IP_ADDRESS Ip;
  34. UINT8 IpMode;
  35. ConfigNvData = &ConfigData->SessionConfigData;
  36. //
  37. // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
  38. //
  39. IScsiRootPathIdLen = (UINT8)AsciiStrLen (ISCSI_ROOT_PATH_ID);
  40. if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
  41. return EFI_NOT_FOUND;
  42. }
  43. //
  44. // Skip the iSCSI RootPath ID "iscsi:".
  45. //
  46. RootPath += IScsiRootPathIdLen;
  47. Length = (UINT8)(Length - IScsiRootPathIdLen);
  48. TmpStr = (CHAR8 *)AllocatePool (Length + 1);
  49. if (TmpStr == NULL) {
  50. return EFI_OUT_OF_RESOURCES;
  51. }
  52. CopyMem (TmpStr, RootPath, Length);
  53. TmpStr[Length] = '\0';
  54. Index = 0;
  55. FieldIndex = RP_FIELD_IDX_SERVERNAME;
  56. ZeroMem (&Fields[0], sizeof (Fields));
  57. //
  58. // Extract the fields in the Root Path option string.
  59. //
  60. for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
  61. if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
  62. Fields[FieldIndex].Str = &TmpStr[Index];
  63. }
  64. while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
  65. Index++;
  66. }
  67. if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
  68. if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
  69. TmpStr[Index] = '\0';
  70. Index++;
  71. }
  72. if (Fields[FieldIndex].Str != NULL) {
  73. Fields[FieldIndex].Len = (UINT8)AsciiStrLen (Fields[FieldIndex].Str);
  74. }
  75. }
  76. }
  77. if (FieldIndex != RP_FIELD_IDX_MAX) {
  78. Status = EFI_INVALID_PARAMETER;
  79. goto ON_EXIT;
  80. }
  81. if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
  82. (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
  83. (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
  84. )
  85. {
  86. Status = EFI_INVALID_PARAMETER;
  87. goto ON_EXIT;
  88. }
  89. //
  90. // Get the IP address of the target.
  91. //
  92. Field = &Fields[RP_FIELD_IDX_SERVERNAME];
  93. if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
  94. IpMode = ConfigNvData->IpMode;
  95. } else {
  96. IpMode = ConfigData->AutoConfigureMode;
  97. }
  98. //
  99. // Server name is expressed as domain name, just save it.
  100. //
  101. if ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {
  102. ConfigNvData->DnsMode = TRUE;
  103. if ((Field->Len + 2) > sizeof (ConfigNvData->TargetUrl)) {
  104. return EFI_INVALID_PARAMETER;
  105. }
  106. CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
  107. ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
  108. } else {
  109. ConfigNvData->DnsMode = FALSE;
  110. ZeroMem (ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
  111. Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
  112. CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
  113. if (EFI_ERROR (Status)) {
  114. goto ON_EXIT;
  115. }
  116. }
  117. //
  118. // Check the protocol type.
  119. //
  120. Field = &Fields[RP_FIELD_IDX_PROTOCOL];
  121. if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
  122. Status = EFI_INVALID_PARAMETER;
  123. goto ON_EXIT;
  124. }
  125. //
  126. // Get the port of the iSCSI target.
  127. //
  128. Field = &Fields[RP_FIELD_IDX_PORT];
  129. if (Field->Str != NULL) {
  130. ConfigNvData->TargetPort = (UINT16)AsciiStrDecimalToUintn (Field->Str);
  131. } else {
  132. ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
  133. }
  134. //
  135. // Get the LUN.
  136. //
  137. Field = &Fields[RP_FIELD_IDX_LUN];
  138. if (Field->Str != NULL) {
  139. Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
  140. if (EFI_ERROR (Status)) {
  141. goto ON_EXIT;
  142. }
  143. } else {
  144. ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
  145. }
  146. //
  147. // Get the target iSCSI Name.
  148. //
  149. Field = &Fields[RP_FIELD_IDX_TARGETNAME];
  150. if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
  151. Status = EFI_INVALID_PARAMETER;
  152. goto ON_EXIT;
  153. }
  154. //
  155. // Validate the iSCSI name.
  156. //
  157. Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
  158. if (EFI_ERROR (Status)) {
  159. goto ON_EXIT;
  160. }
  161. AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
  162. ON_EXIT:
  163. FreePool (TmpStr);
  164. return Status;
  165. }
  166. /**
  167. The callback function registered to the DHCP4 instance that is used to select
  168. the qualified DHCP OFFER.
  169. @param[in] This The DHCP4 protocol.
  170. @param[in] Context The context set when configuring the DHCP4 protocol.
  171. @param[in] CurrentState The current state of the DHCP4 protocol.
  172. @param[in] Dhcp4Event The event occurs in the current state.
  173. @param[in] Packet The DHCP packet that is to be sent or was already received.
  174. @param[out] NewPacket The packet used to replace the above Packet.
  175. @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
  176. in the Dhcp4Event.
  177. @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
  178. @retval Others Other errors as indicated.
  179. **/
  180. EFI_STATUS
  181. EFIAPI
  182. IScsiDhcpSelectOffer (
  183. IN EFI_DHCP4_PROTOCOL *This,
  184. IN VOID *Context,
  185. IN EFI_DHCP4_STATE CurrentState,
  186. IN EFI_DHCP4_EVENT Dhcp4Event,
  187. IN EFI_DHCP4_PACKET *Packet OPTIONAL,
  188. OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
  189. )
  190. {
  191. EFI_STATUS Status;
  192. UINT32 OptionCount;
  193. EFI_DHCP4_PACKET_OPTION **OptionList;
  194. UINT32 Index;
  195. if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
  196. return EFI_SUCCESS;
  197. }
  198. OptionCount = 0;
  199. Status = This->Parse (This, Packet, &OptionCount, NULL);
  200. if (Status != EFI_BUFFER_TOO_SMALL) {
  201. return EFI_NOT_READY;
  202. }
  203. OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
  204. if (OptionList == NULL) {
  205. return EFI_NOT_READY;
  206. }
  207. Status = This->Parse (This, Packet, &OptionCount, OptionList);
  208. if (EFI_ERROR (Status)) {
  209. FreePool (OptionList);
  210. return EFI_NOT_READY;
  211. }
  212. for (Index = 0; Index < OptionCount; Index++) {
  213. if (OptionList[Index]->OpCode != DHCP4_TAG_ROOTPATH) {
  214. continue;
  215. }
  216. Status = IScsiDhcpExtractRootPath (
  217. (CHAR8 *)&OptionList[Index]->Data[0],
  218. OptionList[Index]->Length,
  219. (ISCSI_ATTEMPT_CONFIG_NVDATA *)Context
  220. );
  221. break;
  222. }
  223. if (Index == OptionCount) {
  224. Status = EFI_NOT_READY;
  225. }
  226. FreePool (OptionList);
  227. return Status;
  228. }
  229. /**
  230. Parse the DHCP ACK to get the address configuration and DNS information.
  231. @param[in] Dhcp4 The DHCP4 protocol.
  232. @param[in, out] ConfigData The session configuration data.
  233. @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
  234. @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.
  235. @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
  236. @retval EFI_DEVICE_ERROR Other errors as indicated.
  237. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  238. **/
  239. EFI_STATUS
  240. IScsiParseDhcpAck (
  241. IN EFI_DHCP4_PROTOCOL *Dhcp4,
  242. IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
  243. )
  244. {
  245. EFI_STATUS Status;
  246. EFI_DHCP4_MODE_DATA Dhcp4ModeData;
  247. UINT32 OptionCount;
  248. EFI_DHCP4_PACKET_OPTION **OptionList;
  249. UINT32 Index;
  250. ISCSI_SESSION_CONFIG_NVDATA *NvData;
  251. Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
  252. if (EFI_ERROR (Status)) {
  253. return Status;
  254. }
  255. if (Dhcp4ModeData.State != Dhcp4Bound) {
  256. return EFI_NO_MAPPING;
  257. }
  258. NvData = &ConfigData->SessionConfigData;
  259. CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
  260. CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
  261. CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
  262. OptionCount = 0;
  263. OptionList = NULL;
  264. Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
  265. if (Status != EFI_BUFFER_TOO_SMALL) {
  266. return EFI_DEVICE_ERROR;
  267. }
  268. OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
  269. if (OptionList == NULL) {
  270. return EFI_OUT_OF_RESOURCES;
  271. }
  272. Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
  273. if (EFI_ERROR (Status)) {
  274. FreePool (OptionList);
  275. return EFI_DEVICE_ERROR;
  276. }
  277. for (Index = 0; Index < OptionCount; Index++) {
  278. //
  279. // Get DNS server addresses and DHCP server address from this offer.
  280. //
  281. if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
  282. if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
  283. Status = EFI_INVALID_PARAMETER;
  284. break;
  285. }
  286. //
  287. // Primary DNS server address.
  288. //
  289. CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
  290. if (OptionList[Index]->Length > 4) {
  291. //
  292. // Secondary DNS server address.
  293. //
  294. CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
  295. }
  296. } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
  297. if (OptionList[Index]->Length != 4) {
  298. Status = EFI_INVALID_PARAMETER;
  299. break;
  300. }
  301. CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
  302. }
  303. }
  304. FreePool (OptionList);
  305. return Status;
  306. }
  307. /**
  308. This function will switch the IP4 configuration policy to Static.
  309. @param[in] Ip4Config2 Pointer to the IP4 configuration protocol.
  310. @retval EFI_SUCCESS The policy is already configured to static.
  311. @retval Others Other error as indicated.
  312. **/
  313. EFI_STATUS
  314. IScsiSetIp4Policy (
  315. IN EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2
  316. )
  317. {
  318. EFI_IP4_CONFIG2_POLICY Policy;
  319. EFI_STATUS Status;
  320. UINTN DataSize;
  321. DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
  322. Status = Ip4Config2->GetData (
  323. Ip4Config2,
  324. Ip4Config2DataTypePolicy,
  325. &DataSize,
  326. &Policy
  327. );
  328. if (EFI_ERROR (Status)) {
  329. return Status;
  330. }
  331. if (Policy != Ip4Config2PolicyStatic) {
  332. Policy = Ip4Config2PolicyStatic;
  333. Status = Ip4Config2->SetData (
  334. Ip4Config2,
  335. Ip4Config2DataTypePolicy,
  336. sizeof (EFI_IP4_CONFIG2_POLICY),
  337. &Policy
  338. );
  339. if (EFI_ERROR (Status)) {
  340. return Status;
  341. }
  342. }
  343. return EFI_SUCCESS;
  344. }
  345. /**
  346. Parse the DHCP ACK to get the address configuration and DNS information.
  347. @param[in] Image The handle of the driver image.
  348. @param[in] Controller The handle of the controller.
  349. @param[in, out] ConfigData The attempt configuration data.
  350. @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
  351. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  352. @retval EFI_NO_MEDIA There was a media error.
  353. @retval Others Other errors as indicated.
  354. **/
  355. EFI_STATUS
  356. IScsiDoDhcp (
  357. IN EFI_HANDLE Image,
  358. IN EFI_HANDLE Controller,
  359. IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
  360. )
  361. {
  362. EFI_HANDLE Dhcp4Handle;
  363. EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
  364. EFI_DHCP4_PROTOCOL *Dhcp4;
  365. EFI_STATUS Status;
  366. EFI_DHCP4_PACKET_OPTION *ParaList;
  367. EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
  368. ISCSI_SESSION_CONFIG_NVDATA *NvData;
  369. EFI_STATUS MediaStatus;
  370. Dhcp4Handle = NULL;
  371. Ip4Config2 = NULL;
  372. Dhcp4 = NULL;
  373. ParaList = NULL;
  374. //
  375. // Check media status before doing DHCP.
  376. //
  377. MediaStatus = EFI_SUCCESS;
  378. NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus);
  379. if (MediaStatus != EFI_SUCCESS) {
  380. AsciiPrint ("\n Error: Could not detect network connection.\n");
  381. return EFI_NO_MEDIA;
  382. }
  383. //
  384. // DHCP4 service allows only one of its children to be configured in
  385. // the active state, If the DHCP4 D.O.R.A started by IP4 auto
  386. // configuration and has not been completed, the Dhcp4 state machine
  387. // will not be in the right state for the iSCSI to start a new round D.O.R.A.
  388. // So, we need to switch its policy to static.
  389. //
  390. Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **)&Ip4Config2);
  391. if (!EFI_ERROR (Status)) {
  392. Status = IScsiSetIp4Policy (Ip4Config2);
  393. if (EFI_ERROR (Status)) {
  394. return Status;
  395. }
  396. }
  397. //
  398. // Create a DHCP4 child instance and get the protocol.
  399. //
  400. Status = NetLibCreateServiceChild (
  401. Controller,
  402. Image,
  403. &gEfiDhcp4ServiceBindingProtocolGuid,
  404. &Dhcp4Handle
  405. );
  406. if (EFI_ERROR (Status)) {
  407. return Status;
  408. }
  409. Status = gBS->OpenProtocol (
  410. Dhcp4Handle,
  411. &gEfiDhcp4ProtocolGuid,
  412. (VOID **)&Dhcp4,
  413. Image,
  414. Controller,
  415. EFI_OPEN_PROTOCOL_BY_DRIVER
  416. );
  417. if (EFI_ERROR (Status)) {
  418. goto ON_EXIT;
  419. }
  420. NvData = &ConfigData->SessionConfigData;
  421. ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
  422. if (ParaList == NULL) {
  423. Status = EFI_OUT_OF_RESOURCES;
  424. goto ON_EXIT;
  425. }
  426. //
  427. // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
  428. //
  429. ParaList->OpCode = DHCP4_TAG_PARA_LIST;
  430. ParaList->Length = (UINT8)(NvData->TargetInfoFromDhcp ? 4 : 3);
  431. ParaList->Data[0] = DHCP4_TAG_NETMASK;
  432. ParaList->Data[1] = DHCP4_TAG_ROUTER;
  433. ParaList->Data[2] = DHCP4_TAG_DNS_SERVER;
  434. ParaList->Data[3] = DHCP4_TAG_ROOTPATH;
  435. ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
  436. Dhcp4ConfigData.OptionCount = 1;
  437. Dhcp4ConfigData.OptionList = &ParaList;
  438. if (NvData->TargetInfoFromDhcp) {
  439. //
  440. // Use callback to select an offer that contains target information.
  441. //
  442. Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
  443. Dhcp4ConfigData.CallbackContext = ConfigData;
  444. }
  445. Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
  446. if (EFI_ERROR (Status)) {
  447. goto ON_EXIT;
  448. }
  449. Status = Dhcp4->Start (Dhcp4, NULL);
  450. if (EFI_ERROR (Status)) {
  451. goto ON_EXIT;
  452. }
  453. //
  454. // Parse the ACK to get required information.
  455. //
  456. Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
  457. ON_EXIT:
  458. if (ParaList != NULL) {
  459. FreePool (ParaList);
  460. }
  461. if (Dhcp4 != NULL) {
  462. Dhcp4->Stop (Dhcp4);
  463. Dhcp4->Configure (Dhcp4, NULL);
  464. gBS->CloseProtocol (
  465. Dhcp4Handle,
  466. &gEfiDhcp4ProtocolGuid,
  467. Image,
  468. Controller
  469. );
  470. }
  471. NetLibDestroyServiceChild (
  472. Controller,
  473. Image,
  474. &gEfiDhcp4ServiceBindingProtocolGuid,
  475. Dhcp4Handle
  476. );
  477. return Status;
  478. }