IScsiDhcp6.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. /** @file
  2. iSCSI DHCP6 related configuration routines.
  3. Copyright (c) 2009 - 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 from
  9. Boot File Uniform Resource Locator (URL) Option.
  10. @param[in] RootPath The RootPath string.
  11. @param[in] Length Length of the RootPath option payload.
  12. @param[in, out] ConfigData The iSCSI session configuration data read from
  13. nonvolatile device.
  14. @retval EFI_SUCCESS All required information is extracted from the
  15. RootPath option.
  16. @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
  17. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  18. @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
  19. **/
  20. EFI_STATUS
  21. IScsiDhcp6ExtractRootPath (
  22. IN CHAR8 *RootPath,
  23. IN UINT16 Length,
  24. IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
  25. )
  26. {
  27. EFI_STATUS Status;
  28. UINT16 IScsiRootPathIdLen;
  29. CHAR8 *TmpStr;
  30. ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
  31. ISCSI_ROOT_PATH_FIELD *Field;
  32. UINT32 FieldIndex;
  33. UINT8 Index;
  34. ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
  35. EFI_IP_ADDRESS Ip;
  36. UINT8 IpMode;
  37. ConfigNvData = &ConfigData->SessionConfigData;
  38. ConfigNvData->DnsMode = FALSE;
  39. //
  40. // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
  41. //
  42. IScsiRootPathIdLen = (UINT16)AsciiStrLen (ISCSI_ROOT_PATH_ID);
  43. if ((Length <= IScsiRootPathIdLen) ||
  44. (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0))
  45. {
  46. return EFI_NOT_FOUND;
  47. }
  48. //
  49. // Skip the iSCSI RootPath ID "iscsi:".
  50. //
  51. RootPath = RootPath + IScsiRootPathIdLen;
  52. Length = (UINT16)(Length - IScsiRootPathIdLen);
  53. TmpStr = (CHAR8 *)AllocatePool (Length + 1);
  54. if (TmpStr == NULL) {
  55. return EFI_OUT_OF_RESOURCES;
  56. }
  57. CopyMem (TmpStr, RootPath, Length);
  58. TmpStr[Length] = '\0';
  59. Index = 0;
  60. FieldIndex = 0;
  61. ZeroMem (&Fields[0], sizeof (Fields));
  62. //
  63. // Extract SERVERNAME field in the Root Path option.
  64. //
  65. if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {
  66. //
  67. // The servername is expressed as domain name.
  68. //
  69. ConfigNvData->DnsMode = TRUE;
  70. } else {
  71. Index++;
  72. }
  73. Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];
  74. if (!ConfigNvData->DnsMode) {
  75. while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) {
  76. Index++;
  77. }
  78. //
  79. // Skip ']' and ':'.
  80. //
  81. TmpStr[Index] = '\0';
  82. Index += 2;
  83. } else {
  84. while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
  85. Index++;
  86. }
  87. //
  88. // Skip ':'.
  89. //
  90. TmpStr[Index] = '\0';
  91. Index += 1;
  92. }
  93. Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8)AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);
  94. //
  95. // Extract others fields in the Root Path option string.
  96. //
  97. for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
  98. if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
  99. Fields[FieldIndex].Str = &TmpStr[Index];
  100. }
  101. while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
  102. Index++;
  103. }
  104. if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
  105. if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
  106. TmpStr[Index] = '\0';
  107. Index++;
  108. }
  109. if (Fields[FieldIndex].Str != NULL) {
  110. Fields[FieldIndex].Len = (UINT8)AsciiStrLen (Fields[FieldIndex].Str);
  111. }
  112. }
  113. }
  114. if (FieldIndex != RP_FIELD_IDX_MAX) {
  115. Status = EFI_INVALID_PARAMETER;
  116. goto ON_EXIT;
  117. }
  118. if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
  119. (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
  120. (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
  121. )
  122. {
  123. Status = EFI_INVALID_PARAMETER;
  124. goto ON_EXIT;
  125. }
  126. //
  127. // Get the IP address of the target.
  128. //
  129. Field = &Fields[RP_FIELD_IDX_SERVERNAME];
  130. if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
  131. IpMode = ConfigNvData->IpMode;
  132. } else {
  133. IpMode = ConfigData->AutoConfigureMode;
  134. }
  135. //
  136. // Server name is expressed as domain name, just save it.
  137. //
  138. if (ConfigNvData->DnsMode) {
  139. if ((Field->Len + 2) > sizeof (ConfigNvData->TargetUrl)) {
  140. return EFI_INVALID_PARAMETER;
  141. }
  142. CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
  143. ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
  144. } else {
  145. ZeroMem (&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
  146. Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
  147. CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
  148. if (EFI_ERROR (Status)) {
  149. goto ON_EXIT;
  150. }
  151. }
  152. //
  153. // Check the protocol type.
  154. //
  155. Field = &Fields[RP_FIELD_IDX_PROTOCOL];
  156. if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
  157. Status = EFI_INVALID_PARAMETER;
  158. goto ON_EXIT;
  159. }
  160. //
  161. // Get the port of the iSCSI target.
  162. //
  163. Field = &Fields[RP_FIELD_IDX_PORT];
  164. if (Field->Str != NULL) {
  165. ConfigNvData->TargetPort = (UINT16)AsciiStrDecimalToUintn (Field->Str);
  166. } else {
  167. ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
  168. }
  169. //
  170. // Get the LUN.
  171. //
  172. Field = &Fields[RP_FIELD_IDX_LUN];
  173. if (Field->Str != NULL) {
  174. Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
  175. if (EFI_ERROR (Status)) {
  176. goto ON_EXIT;
  177. }
  178. } else {
  179. ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
  180. }
  181. //
  182. // Get the target iSCSI Name.
  183. //
  184. Field = &Fields[RP_FIELD_IDX_TARGETNAME];
  185. if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
  186. Status = EFI_INVALID_PARAMETER;
  187. goto ON_EXIT;
  188. }
  189. //
  190. // Validate the iSCSI name.
  191. //
  192. Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
  193. if (EFI_ERROR (Status)) {
  194. goto ON_EXIT;
  195. }
  196. AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
  197. ON_EXIT:
  198. FreePool (TmpStr);
  199. return Status;
  200. }
  201. /**
  202. EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
  203. instance to intercept events that occurs in the DHCPv6 Information Request
  204. exchange process.
  205. @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
  206. is used to configure this callback function.
  207. @param[in] Context Pointer to the context that is initialized in
  208. the EFI_DHCP6_PROTOCOL.InfoRequest().
  209. @param[in] Packet Pointer to Reply packet that has been received.
  210. The EFI DHCPv6 Protocol instance is responsible
  211. for freeing the buffer.
  212. @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
  213. Information Request exchange process.
  214. @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
  215. Information Request exchange process.
  216. @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
  217. the Information Request exchange process.
  218. @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
  219. the Information Request exchange process because some
  220. request information are not received.
  221. **/
  222. EFI_STATUS
  223. EFIAPI
  224. IScsiDhcp6ParseReply (
  225. IN EFI_DHCP6_PROTOCOL *This,
  226. IN VOID *Context,
  227. IN EFI_DHCP6_PACKET *Packet
  228. )
  229. {
  230. EFI_STATUS Status;
  231. UINT32 Index;
  232. UINT32 OptionCount;
  233. EFI_DHCP6_PACKET_OPTION *BootFileOpt;
  234. EFI_DHCP6_PACKET_OPTION **OptionList;
  235. ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;
  236. UINT16 ParaLen;
  237. OptionCount = 0;
  238. BootFileOpt = NULL;
  239. Status = This->Parse (This, Packet, &OptionCount, NULL);
  240. if (Status != EFI_BUFFER_TOO_SMALL) {
  241. return EFI_NOT_READY;
  242. }
  243. OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
  244. if (OptionList == NULL) {
  245. return EFI_NOT_READY;
  246. }
  247. Status = This->Parse (This, Packet, &OptionCount, OptionList);
  248. if (EFI_ERROR (Status)) {
  249. Status = EFI_NOT_READY;
  250. goto Exit;
  251. }
  252. ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *)Context;
  253. for (Index = 0; Index < OptionCount; Index++) {
  254. OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
  255. OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);
  256. //
  257. // Get DNS server addresses from this reply packet.
  258. //
  259. if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {
  260. if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
  261. Status = EFI_UNSUPPORTED;
  262. goto Exit;
  263. }
  264. //
  265. // Primary DNS server address.
  266. //
  267. CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));
  268. if (OptionList[Index]->OpLen > 16) {
  269. //
  270. // Secondary DNS server address
  271. //
  272. CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));
  273. }
  274. } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {
  275. //
  276. // The server sends this option to inform the client about an URL to a boot file.
  277. //
  278. BootFileOpt = OptionList[Index];
  279. } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARAM) {
  280. //
  281. // The server sends this option to inform the client about DHCP6 server address.
  282. //
  283. if (OptionList[Index]->OpLen < 18) {
  284. Status = EFI_UNSUPPORTED;
  285. goto Exit;
  286. }
  287. //
  288. // Check param-len 1, should be 16 bytes.
  289. //
  290. CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16));
  291. if (NTOHS (ParaLen) != 16) {
  292. Status = EFI_UNSUPPORTED;
  293. goto Exit;
  294. }
  295. CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS));
  296. }
  297. }
  298. if (BootFileOpt == NULL) {
  299. Status = EFI_UNSUPPORTED;
  300. goto Exit;
  301. }
  302. //
  303. // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
  304. //
  305. Status = IScsiDhcp6ExtractRootPath (
  306. (CHAR8 *)BootFileOpt->Data,
  307. BootFileOpt->OpLen,
  308. ConfigData
  309. );
  310. Exit:
  311. FreePool (OptionList);
  312. return Status;
  313. }
  314. /**
  315. Parse the DHCP ACK to get the address configuration and DNS information.
  316. @param[in] Image The handle of the driver image.
  317. @param[in] Controller The handle of the controller;
  318. @param[in, out] ConfigData The attempt configuration data.
  319. @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
  320. @retval EFI_NO_MAPPING DHCP failed to acquire address and other
  321. information.
  322. @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
  323. @retval EFI_DEVICE_ERROR Some unexpected error occurred.
  324. @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
  325. operation.
  326. @retval EFI_NO_MEDIA There was a media error.
  327. **/
  328. EFI_STATUS
  329. IScsiDoDhcp6 (
  330. IN EFI_HANDLE Image,
  331. IN EFI_HANDLE Controller,
  332. IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
  333. )
  334. {
  335. EFI_HANDLE Dhcp6Handle;
  336. EFI_DHCP6_PROTOCOL *Dhcp6;
  337. EFI_STATUS Status;
  338. EFI_STATUS TimerStatus;
  339. EFI_DHCP6_PACKET_OPTION *Oro;
  340. EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
  341. EFI_EVENT Timer;
  342. EFI_STATUS MediaStatus;
  343. //
  344. // Check media status before doing DHCP.
  345. //
  346. MediaStatus = EFI_SUCCESS;
  347. NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus);
  348. if (MediaStatus != EFI_SUCCESS) {
  349. AsciiPrint ("\n Error: Could not detect network connection.\n");
  350. return EFI_NO_MEDIA;
  351. }
  352. //
  353. // iSCSI will only request target info from DHCPv6 server.
  354. //
  355. if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {
  356. return EFI_SUCCESS;
  357. }
  358. Dhcp6Handle = NULL;
  359. Dhcp6 = NULL;
  360. Oro = NULL;
  361. Timer = NULL;
  362. //
  363. // Create a DHCP6 child instance and get the protocol.
  364. //
  365. Status = NetLibCreateServiceChild (
  366. Controller,
  367. Image,
  368. &gEfiDhcp6ServiceBindingProtocolGuid,
  369. &Dhcp6Handle
  370. );
  371. if (EFI_ERROR (Status)) {
  372. return Status;
  373. }
  374. Status = gBS->OpenProtocol (
  375. Dhcp6Handle,
  376. &gEfiDhcp6ProtocolGuid,
  377. (VOID **)&Dhcp6,
  378. Image,
  379. Controller,
  380. EFI_OPEN_PROTOCOL_BY_DRIVER
  381. );
  382. if (EFI_ERROR (Status)) {
  383. goto ON_EXIT;
  384. }
  385. Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5);
  386. if (Oro == NULL) {
  387. Status = EFI_OUT_OF_RESOURCES;
  388. goto ON_EXIT;
  389. }
  390. //
  391. // Ask the server to reply with DNS and Boot File URL options by info request.
  392. // All members in EFI_DHCP6_PACKET_OPTION are in network order.
  393. //
  394. Oro->OpCode = HTONS (DHCP6_OPT_ORO);
  395. Oro->OpLen = HTONS (2 * 3);
  396. Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;
  397. Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;
  398. Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARAM;
  399. InfoReqReXmit.Irt = 4;
  400. InfoReqReXmit.Mrc = 1;
  401. InfoReqReXmit.Mrt = 10;
  402. InfoReqReXmit.Mrd = 30;
  403. Status = Dhcp6->InfoRequest (
  404. Dhcp6,
  405. TRUE,
  406. Oro,
  407. 0,
  408. NULL,
  409. &InfoReqReXmit,
  410. NULL,
  411. IScsiDhcp6ParseReply,
  412. ConfigData
  413. );
  414. if (Status == EFI_NO_MAPPING) {
  415. Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
  416. if (EFI_ERROR (Status)) {
  417. goto ON_EXIT;
  418. }
  419. Status = gBS->SetTimer (
  420. Timer,
  421. TimerRelative,
  422. ISCSI_GET_MAPPING_TIMEOUT
  423. );
  424. if (EFI_ERROR (Status)) {
  425. goto ON_EXIT;
  426. }
  427. do {
  428. TimerStatus = gBS->CheckEvent (Timer);
  429. if (!EFI_ERROR (TimerStatus)) {
  430. Status = Dhcp6->InfoRequest (
  431. Dhcp6,
  432. TRUE,
  433. Oro,
  434. 0,
  435. NULL,
  436. &InfoReqReXmit,
  437. NULL,
  438. IScsiDhcp6ParseReply,
  439. ConfigData
  440. );
  441. }
  442. } while (TimerStatus == EFI_NOT_READY);
  443. }
  444. ON_EXIT:
  445. if (Oro != NULL) {
  446. FreePool (Oro);
  447. }
  448. if (Timer != NULL) {
  449. gBS->CloseEvent (Timer);
  450. }
  451. if (Dhcp6 != NULL) {
  452. gBS->CloseProtocol (
  453. Dhcp6Handle,
  454. &gEfiDhcp6ProtocolGuid,
  455. Image,
  456. Controller
  457. );
  458. }
  459. NetLibDestroyServiceChild (
  460. Controller,
  461. Image,
  462. &gEfiDhcp6ServiceBindingProtocolGuid,
  463. Dhcp6Handle
  464. );
  465. return Status;
  466. }