Dhcp4Option.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. /** @file
  2. Function to validate, parse, process the DHCP options.
  3. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "Dhcp4Impl.h"
  7. ///
  8. /// A list of the format of DHCP Options sorted by option tag
  9. /// to validate a dhcp message. Refere the comments of the
  10. /// DHCP_OPTION_FORMAT structure.
  11. ///
  12. DHCP_OPTION_FORMAT DhcpOptionFormats[] = {
  13. { DHCP4_TAG_NETMASK, DHCP_OPTION_IP, 1, 1, TRUE },
  14. { DHCP4_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1, FALSE },
  15. { DHCP4_TAG_ROUTER, DHCP_OPTION_IP, 1, -1, TRUE },
  16. { DHCP4_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  17. { DHCP4_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  18. { DHCP4_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  19. { DHCP4_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  20. { DHCP4_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  21. { DHCP4_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  22. { DHCP4_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  23. { DHCP4_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  24. { DHCP4_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1, FALSE },
  25. { DHCP4_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1, FALSE },
  26. { DHCP4_TAG_DUMP, DHCP_OPTION_INT8, 1, -1, FALSE },
  27. { DHCP4_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1, FALSE },
  28. { DHCP4_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1, FALSE },
  29. { DHCP4_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1, FALSE },
  30. { DHCP4_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1, FALSE },
  31. { DHCP4_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  32. { DHCP4_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  33. { DHCP4_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1, FALSE },
  34. { DHCP4_TAG_EMTU, DHCP_OPTION_INT16, 1, 1, FALSE },
  35. { DHCP4_TAG_TTL, DHCP_OPTION_INT8, 1, 1, FALSE },
  36. { DHCP4_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1, FALSE },
  37. { DHCP4_TAG_PATHMTU_PLATEAU, DHCP_OPTION_INT16, 1, -1, FALSE },
  38. { DHCP4_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1, FALSE },
  39. { DHCP4_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  40. { DHCP4_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1, FALSE },
  41. { DHCP4_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  42. { DHCP4_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  43. { DHCP4_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  44. { DHCP4_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1, FALSE },
  45. { DHCP4_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1, FALSE },
  46. { DHCP4_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  47. { DHCP4_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1, FALSE },
  48. { DHCP4_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  49. { DHCP4_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1, FALSE },
  50. { DHCP4_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1, FALSE },
  51. { DHCP4_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1, FALSE },
  52. { DHCP4_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1, FALSE },
  53. { DHCP4_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  54. { DHCP4_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  55. { DHCP4_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1, FALSE },
  56. { DHCP4_TAG_NBNS, DHCP_OPTION_IP, 1, -1, FALSE },
  57. { DHCP4_TAG_NBDD, DHCP_OPTION_IP, 1, -1, FALSE },
  58. { DHCP4_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1, FALSE },
  59. { DHCP4_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1, FALSE },
  60. { DHCP4_TAG_XFONT, DHCP_OPTION_IP, 1, -1, FALSE },
  61. { DHCP4_TAG_XDM, DHCP_OPTION_IP, 1, -1, FALSE },
  62. { DHCP4_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1, FALSE },
  63. { DHCP4_TAG_LEASE, DHCP_OPTION_INT32, 1, 1, TRUE },
  64. { DHCP4_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1, TRUE },
  65. { DHCP4_TAG_MSG_TYPE, DHCP_OPTION_INT8, 1, 1, TRUE },
  66. { DHCP4_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1, TRUE },
  67. { DHCP4_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1, FALSE },
  68. { DHCP4_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1, FALSE },
  69. { DHCP4_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1, FALSE },
  70. { DHCP4_TAG_T1, DHCP_OPTION_INT32, 1, 1, TRUE },
  71. { DHCP4_TAG_T2, DHCP_OPTION_INT32, 1, 1, TRUE },
  72. { DHCP4_TAG_VENDOR_CLASS_ID, DHCP_OPTION_INT8, 1, -1, FALSE },
  73. { DHCP4_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1, FALSE },
  74. { DHCP4_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1, FALSE },
  75. { DHCP4_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1, FALSE },
  76. { DHCP4_TAG_TFTP, DHCP_OPTION_INT8, 1, -1, FALSE },
  77. { DHCP4_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1, FALSE },
  78. { DHCP4_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1, FALSE },
  79. { DHCP4_TAG_SMTP, DHCP_OPTION_IP, 1, -1, FALSE },
  80. { DHCP4_TAG_POP3, DHCP_OPTION_IP, 1, -1, FALSE },
  81. { DHCP4_TAG_NNTP, DHCP_OPTION_IP, 1, -1, FALSE },
  82. { DHCP4_TAG_WWW, DHCP_OPTION_IP, 1, -1, FALSE },
  83. { DHCP4_TAG_FINGER, DHCP_OPTION_IP, 1, -1, FALSE },
  84. { DHCP4_TAG_IRC, DHCP_OPTION_IP, 1, -1, FALSE },
  85. { DHCP4_TAG_STTALK, DHCP_OPTION_IP, 1, -1, FALSE },
  86. { DHCP4_TAG_STDA, DHCP_OPTION_IP, 1, -1, FALSE },
  87. { DHCP4_TAG_CLASSLESS_ROUTE, DHCP_OPTION_INT8, 5, -1, FALSE },
  88. };
  89. /**
  90. Binary search the DhcpOptionFormats array to find the format
  91. information about a specific option.
  92. @param[in] Tag The option's tag.
  93. @return The point to the option's format, NULL if not found.
  94. **/
  95. DHCP_OPTION_FORMAT *
  96. DhcpFindOptionFormat (
  97. IN UINT8 Tag
  98. )
  99. {
  100. INTN Left;
  101. INTN Right;
  102. INTN Middle;
  103. Left = 0;
  104. Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
  105. while (Right >= Left) {
  106. Middle = (Left + Right) / 2;
  107. if (Tag == DhcpOptionFormats[Middle].Tag) {
  108. return &DhcpOptionFormats[Middle];
  109. }
  110. if (Tag < DhcpOptionFormats[Middle].Tag) {
  111. Right = Middle - 1;
  112. } else {
  113. Left = Middle + 1;
  114. }
  115. }
  116. return NULL;
  117. }
  118. /**
  119. Validate whether a single DHCP option is valid according to its format.
  120. @param[in] Format The option's format
  121. @param[in] OptValue The value of the option
  122. @param[in] Len The length of the option value
  123. @retval TRUE The option is valid.
  124. @retval FALSE Otherwise.
  125. **/
  126. BOOLEAN
  127. DhcpOptionIsValid (
  128. IN DHCP_OPTION_FORMAT *Format,
  129. IN UINT8 *OptValue,
  130. IN INTN Len
  131. )
  132. {
  133. INTN Unit;
  134. INTN Occur;
  135. INTN Index;
  136. Unit = 0;
  137. switch (Format->Type) {
  138. case DHCP_OPTION_SWITCH:
  139. case DHCP_OPTION_INT8:
  140. Unit = 1;
  141. break;
  142. case DHCP_OPTION_INT16:
  143. Unit = 2;
  144. break;
  145. case DHCP_OPTION_INT32:
  146. case DHCP_OPTION_IP:
  147. Unit = 4;
  148. break;
  149. case DHCP_OPTION_IPPAIR:
  150. Unit = 8;
  151. break;
  152. }
  153. ASSERT (Unit != 0);
  154. //
  155. // Validate that the option appears in the full units.
  156. //
  157. if ((Len % Unit) != 0) {
  158. return FALSE;
  159. }
  160. //
  161. // Validate the occurrence of the option unit is with in [MinOccur, MaxOccur]
  162. //
  163. Occur = Len / Unit;
  164. if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
  165. ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))
  166. )
  167. {
  168. return FALSE;
  169. }
  170. //
  171. // If the option is of type switch, only 0/1 are valid values.
  172. //
  173. if (Format->Type == DHCP_OPTION_SWITCH) {
  174. for (Index = 0; Index < Occur; Index++) {
  175. if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
  176. return FALSE;
  177. }
  178. }
  179. }
  180. return TRUE;
  181. }
  182. /**
  183. Extract the client interested options, all the parameters are
  184. converted to host byte order.
  185. @param[in] Tag The DHCP option tag
  186. @param[in] Len The length of the option
  187. @param[in] Data The value of the DHCP option
  188. @param[out] Para The variable to save the interested parameter
  189. @retval EFI_SUCCESS The DHCP option is successfully extracted.
  190. @retval EFI_INVALID_PARAMETER The DHCP option is mal-formatted
  191. **/
  192. EFI_STATUS
  193. DhcpGetParameter (
  194. IN UINT8 Tag,
  195. IN INTN Len,
  196. IN UINT8 *Data,
  197. OUT DHCP_PARAMETER *Para
  198. )
  199. {
  200. switch (Tag) {
  201. case DHCP4_TAG_NETMASK:
  202. Para->NetMask = NetGetUint32 (Data);
  203. break;
  204. case DHCP4_TAG_ROUTER:
  205. //
  206. // Return the first router to consumer which is the preferred one
  207. //
  208. Para->Router = NetGetUint32 (Data);
  209. break;
  210. case DHCP4_TAG_LEASE:
  211. Para->Lease = NetGetUint32 (Data);
  212. break;
  213. case DHCP4_TAG_OVERLOAD:
  214. Para->Overload = *Data;
  215. if ((Para->Overload < 1) || (Para->Overload > 3)) {
  216. return EFI_INVALID_PARAMETER;
  217. }
  218. break;
  219. case DHCP4_TAG_MSG_TYPE:
  220. Para->DhcpType = *Data;
  221. if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
  222. return EFI_INVALID_PARAMETER;
  223. }
  224. break;
  225. case DHCP4_TAG_SERVER_ID:
  226. Para->ServerId = NetGetUint32 (Data);
  227. break;
  228. case DHCP4_TAG_T1:
  229. Para->T1 = NetGetUint32 (Data);
  230. break;
  231. case DHCP4_TAG_T2:
  232. Para->T2 = NetGetUint32 (Data);
  233. break;
  234. }
  235. return EFI_SUCCESS;
  236. }
  237. /**
  238. Inspect all the options in a single buffer. DHCP options may be contained
  239. in several buffers, such as the BOOTP options filed, boot file or server
  240. name. Each option buffer is required to end with DHCP4_TAG_EOP.
  241. @param[in] Buffer The buffer which contains DHCP options
  242. @param[in] BufLen The length of the buffer
  243. @param[in] Check The callback function for each option found
  244. @param[in] Context The opaque parameter for the Check
  245. @param[out] Overload Variable to save the value of DHCP4_TAG_OVERLOAD
  246. option.
  247. @retval EFI_SUCCESS All the options are valid
  248. @retval EFI_INVALID_PARAMETER The options are mal-formatted.
  249. **/
  250. EFI_STATUS
  251. DhcpIterateBufferOptions (
  252. IN UINT8 *Buffer,
  253. IN INTN BufLen,
  254. IN DHCP_CHECK_OPTION Check OPTIONAL,
  255. IN VOID *Context,
  256. OUT UINT8 *Overload OPTIONAL
  257. )
  258. {
  259. INTN Cur;
  260. UINT8 Tag;
  261. UINT8 Len;
  262. Cur = 0;
  263. while (Cur < BufLen) {
  264. Tag = Buffer[Cur];
  265. if (Tag == DHCP4_TAG_PAD) {
  266. Cur++;
  267. continue;
  268. } else if (Tag == DHCP4_TAG_EOP) {
  269. return EFI_SUCCESS;
  270. }
  271. Cur++;
  272. if (Cur == BufLen) {
  273. return EFI_INVALID_PARAMETER;
  274. }
  275. Len = Buffer[Cur++];
  276. if (Cur + Len > BufLen) {
  277. return EFI_INVALID_PARAMETER;
  278. }
  279. if ((Tag == DHCP4_TAG_OVERLOAD) && (Overload != NULL)) {
  280. if (Len != 1) {
  281. return EFI_INVALID_PARAMETER;
  282. }
  283. *Overload = Buffer[Cur];
  284. }
  285. if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
  286. return EFI_INVALID_PARAMETER;
  287. }
  288. Cur += Len;
  289. }
  290. //
  291. // Each option buffer is expected to end with an EOP
  292. //
  293. return EFI_INVALID_PARAMETER;
  294. }
  295. /**
  296. Iterate through a DHCP message to visit each option. First inspect
  297. all the options in the OPTION field. Then if overloaded, inspect
  298. the options in FILENAME and SERVERNAME fields. One option may be
  299. encoded in several places. See RFC 3396 Encoding Long Options in DHCP
  300. @param[in] Packet The DHCP packet to check the options for
  301. @param[in] Check The callback function to be called for each option
  302. found
  303. @param[in] Context The opaque parameter for Check
  304. @retval EFI_SUCCESS The DHCP packet's options are well formatted
  305. @retval EFI_INVALID_PARAMETER The DHCP packet's options are not well formatted
  306. **/
  307. EFI_STATUS
  308. DhcpIterateOptions (
  309. IN EFI_DHCP4_PACKET *Packet,
  310. IN DHCP_CHECK_OPTION Check OPTIONAL,
  311. IN VOID *Context
  312. )
  313. {
  314. EFI_STATUS Status;
  315. UINT8 Overload;
  316. Overload = 0;
  317. Status = DhcpIterateBufferOptions (
  318. Packet->Dhcp4.Option,
  319. Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
  320. Check,
  321. Context,
  322. &Overload
  323. );
  324. if (EFI_ERROR (Status)) {
  325. return Status;
  326. }
  327. if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
  328. Status = DhcpIterateBufferOptions (
  329. (UINT8 *)Packet->Dhcp4.Header.BootFileName,
  330. 128,
  331. Check,
  332. Context,
  333. NULL
  334. );
  335. if (EFI_ERROR (Status)) {
  336. return Status;
  337. }
  338. }
  339. if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
  340. Status = DhcpIterateBufferOptions (
  341. (UINT8 *)Packet->Dhcp4.Header.ServerName,
  342. 64,
  343. Check,
  344. Context,
  345. NULL
  346. );
  347. if (EFI_ERROR (Status)) {
  348. return Status;
  349. }
  350. }
  351. return EFI_SUCCESS;
  352. }
  353. /**
  354. Call back function to DhcpIterateOptions to compute each option's
  355. length. It just adds the data length of all the occurrences of this
  356. Tag. Context is an array of 256 DHCP_OPTION_COUNT.
  357. @param[in] Tag The current option to check
  358. @param[in] Len The length of the option data
  359. @param[in] Data The option data
  360. @param[in] Context The context, which is a array of 256
  361. DHCP_OPTION_COUNT.
  362. @retval EFI_SUCCESS It always returns EFI_SUCCESS.
  363. **/
  364. EFI_STATUS
  365. DhcpGetOptionLen (
  366. IN UINT8 Tag,
  367. IN UINT8 Len,
  368. IN UINT8 *Data,
  369. IN VOID *Context
  370. )
  371. {
  372. DHCP_OPTION_COUNT *OpCount;
  373. OpCount = (DHCP_OPTION_COUNT *)Context;
  374. OpCount[Tag].Offset = (UINT16)(OpCount[Tag].Offset + Len);
  375. return EFI_SUCCESS;
  376. }
  377. /**
  378. Call back function to DhcpIterateOptions to consolidate each option's
  379. data. There are maybe several occurrence of the same option.
  380. @param[in] Tag The option to consolidate its data
  381. @param[in] Len The length of option data
  382. @param[in] Data The data of the option's current occurrence
  383. @param[in] Context The context, which is DHCP_OPTION_CONTEXT. This
  384. array is just a wrap to pass THREE parameters.
  385. @retval EFI_SUCCESS It always returns EFI_SUCCESS
  386. **/
  387. EFI_STATUS
  388. DhcpFillOption (
  389. IN UINT8 Tag,
  390. IN UINT8 Len,
  391. IN UINT8 *Data,
  392. IN VOID *Context
  393. )
  394. {
  395. DHCP_OPTION_CONTEXT *OptContext;
  396. DHCP_OPTION_COUNT *OptCount;
  397. DHCP_OPTION *Options;
  398. UINT8 *Buf;
  399. UINT8 Index;
  400. OptContext = (DHCP_OPTION_CONTEXT *)Context;
  401. OptCount = OptContext->OpCount;
  402. Index = OptCount[Tag].Index;
  403. Options = OptContext->Options;
  404. Buf = OptContext->Buf;
  405. if (Options[Index].Data == NULL) {
  406. Options[Index].Tag = Tag;
  407. Options[Index].Data = Buf + OptCount[Tag].Offset;
  408. }
  409. CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
  410. OptCount[Tag].Offset = (UINT16)(OptCount[Tag].Offset + Len);
  411. Options[Index].Len = (UINT16)(Options[Index].Len + Len);
  412. return EFI_SUCCESS;
  413. }
  414. /**
  415. Parse the options of a DHCP packet. It supports RFC 3396: Encoding
  416. Long Options in DHCP. That is, it will combine all the option value
  417. of all the occurrences of each option.
  418. A little bit of implementation:
  419. It adopts the "Key indexed counting" algorithm. First, it allocates
  420. an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
  421. as a UINT8. It then iterates the DHCP packet to get data length of
  422. each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
  423. knows the number of present options and their length. It allocates a
  424. array of DHCP_OPTION and a continuous buffer after the array to put
  425. all the options' data. Each option's data is pointed to by the Data
  426. field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
  427. with DhcpFillOption to fill each option's data to its position in the
  428. buffer.
  429. @param[in] Packet The DHCP packet to parse the options
  430. @param[out] Count The number of valid dhcp options present in the
  431. packet
  432. @param[out] OptionPoint The array that contains the DHCP options. Caller
  433. should free it.
  434. @retval EFI_NOT_FOUND Cannot find any option.
  435. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
  436. @retval EFI_INVALID_PARAMETER The options are mal-formatted
  437. @retval EFI_SUCCESS The options are parsed into OptionPoint
  438. **/
  439. EFI_STATUS
  440. DhcpParseOption (
  441. IN EFI_DHCP4_PACKET *Packet,
  442. OUT INTN *Count,
  443. OUT DHCP_OPTION **OptionPoint
  444. )
  445. {
  446. DHCP_OPTION_CONTEXT Context;
  447. DHCP_OPTION *Options;
  448. DHCP_OPTION_COUNT *OptCount;
  449. EFI_STATUS Status;
  450. UINT16 TotalLen;
  451. INTN OptNum;
  452. INTN Index;
  453. ASSERT ((Count != NULL) && (OptionPoint != NULL));
  454. //
  455. // First compute how many options and how long each option is
  456. // with the "Key indexed counting" algorithms.
  457. //
  458. OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
  459. if (OptCount == NULL) {
  460. return EFI_OUT_OF_RESOURCES;
  461. }
  462. Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
  463. if (EFI_ERROR (Status)) {
  464. goto ON_EXIT;
  465. }
  466. //
  467. // Before the loop, Offset is the length of the option. After loop,
  468. // OptCount[Index].Offset specifies the offset into the continuous
  469. // option value buffer to put the data.
  470. //
  471. TotalLen = 0;
  472. OptNum = 0;
  473. for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
  474. if (OptCount[Index].Offset != 0) {
  475. OptCount[Index].Index = (UINT8)OptNum;
  476. TotalLen = (UINT16)(TotalLen + OptCount[Index].Offset);
  477. OptCount[Index].Offset = (UINT16)(TotalLen - OptCount[Index].Offset);
  478. OptNum++;
  479. }
  480. }
  481. *Count = OptNum;
  482. *OptionPoint = NULL;
  483. if (OptNum == 0) {
  484. goto ON_EXIT;
  485. }
  486. //
  487. // Allocate a buffer to hold the DHCP options, and after that, a
  488. // continuous buffer to put all the options' data.
  489. //
  490. Options = AllocateZeroPool ((UINTN)(OptNum * sizeof (DHCP_OPTION)) + TotalLen);
  491. if (Options == NULL) {
  492. Status = EFI_OUT_OF_RESOURCES;
  493. goto ON_EXIT;
  494. }
  495. Context.OpCount = OptCount;
  496. Context.Options = Options;
  497. Context.Buf = (UINT8 *)(Options + OptNum);
  498. Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
  499. if (EFI_ERROR (Status)) {
  500. FreePool (Options);
  501. goto ON_EXIT;
  502. }
  503. *OptionPoint = Options;
  504. ON_EXIT:
  505. FreePool (OptCount);
  506. return Status;
  507. }
  508. /**
  509. Validate the packet's options. If necessary, allocate
  510. and fill in the interested parameters.
  511. @param[in] Packet The packet to validate the options
  512. @param[out] Para The variable to save the DHCP parameters.
  513. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
  514. @retval EFI_INVALID_PARAMETER The options are mal-formatted
  515. @retval EFI_SUCCESS The options are parsed into OptionPoint
  516. **/
  517. EFI_STATUS
  518. DhcpValidateOptions (
  519. IN EFI_DHCP4_PACKET *Packet,
  520. OUT DHCP_PARAMETER **Para OPTIONAL
  521. )
  522. {
  523. DHCP_PARAMETER Parameter;
  524. DHCP_OPTION_FORMAT *Format;
  525. DHCP_OPTION *AllOption;
  526. DHCP_OPTION *Option;
  527. EFI_STATUS Status;
  528. BOOLEAN Updated;
  529. INTN Count;
  530. INTN Index;
  531. if (Para != NULL) {
  532. *Para = NULL;
  533. }
  534. AllOption = NULL;
  535. Status = DhcpParseOption (Packet, &Count, &AllOption);
  536. if (EFI_ERROR (Status) || (Count == 0)) {
  537. return Status;
  538. }
  539. ASSERT (AllOption != NULL);
  540. Updated = FALSE;
  541. ZeroMem (&Parameter, sizeof (Parameter));
  542. for (Index = 0; Index < Count; Index++) {
  543. Option = &AllOption[Index];
  544. //
  545. // Find the format of the option then validate it.
  546. //
  547. Format = DhcpFindOptionFormat (Option->Tag);
  548. if (Format == NULL) {
  549. continue;
  550. }
  551. if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
  552. Status = EFI_INVALID_PARAMETER;
  553. goto ON_EXIT;
  554. }
  555. //
  556. // Get the client interested parameters
  557. //
  558. if (Format->Alert && (Para != NULL)) {
  559. Updated = TRUE;
  560. Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
  561. if (EFI_ERROR (Status)) {
  562. goto ON_EXIT;
  563. }
  564. }
  565. }
  566. if (Updated && (Para != NULL)) {
  567. *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
  568. if (*Para == NULL) {
  569. Status = EFI_OUT_OF_RESOURCES;
  570. goto ON_EXIT;
  571. }
  572. }
  573. ON_EXIT:
  574. FreePool (AllOption);
  575. return Status;
  576. }
  577. /**
  578. Append an option to the memory, if the option is longer than
  579. 255 bytes, splits it into several options.
  580. @param[out] Buf The buffer to append the option to
  581. @param[in] Tag The option's tag
  582. @param[in] DataLen The length of the option's data
  583. @param[in] Data The option's data
  584. @return The position to append the next option
  585. **/
  586. UINT8 *
  587. DhcpAppendOption (
  588. OUT UINT8 *Buf,
  589. IN UINT8 Tag,
  590. IN UINT16 DataLen,
  591. IN UINT8 *Data
  592. )
  593. {
  594. INTN Index;
  595. INTN Len;
  596. ASSERT (DataLen != 0);
  597. for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
  598. Len = MIN (255, DataLen - Index * 255);
  599. *(Buf++) = Tag;
  600. *(Buf++) = (UINT8)Len;
  601. CopyMem (Buf, Data + Index * 255, (UINTN)Len);
  602. Buf += Len;
  603. }
  604. return Buf;
  605. }
  606. /**
  607. Build a new DHCP packet from a seed packet. Options may be deleted or
  608. appended. The caller should free the NewPacket when finished using it.
  609. @param[in] SeedPacket The seed packet to start with
  610. @param[in] DeleteCount The number of options to delete
  611. @param[in] DeleteList The options to delete from the packet
  612. @param[in] AppendCount The number of options to append
  613. @param[in] AppendList The options to append to the packet
  614. @param[out] NewPacket The new packet, allocated and built by this
  615. function.
  616. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
  617. @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formatted
  618. @retval EFI_SUCCESS The packet is build.
  619. **/
  620. EFI_STATUS
  621. DhcpBuild (
  622. IN EFI_DHCP4_PACKET *SeedPacket,
  623. IN UINT32 DeleteCount,
  624. IN UINT8 *DeleteList OPTIONAL,
  625. IN UINT32 AppendCount,
  626. IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
  627. OUT EFI_DHCP4_PACKET **NewPacket
  628. )
  629. {
  630. DHCP_OPTION *Mark;
  631. DHCP_OPTION *SeedOptions;
  632. EFI_DHCP4_PACKET *Packet;
  633. EFI_STATUS Status;
  634. INTN Count;
  635. UINT32 Index;
  636. UINT32 Len;
  637. UINT8 *Buf;
  638. //
  639. // Use an array of DHCP_OPTION to mark the existence
  640. // and position of each valid options.
  641. //
  642. Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
  643. if (Mark == NULL) {
  644. return EFI_OUT_OF_RESOURCES;
  645. }
  646. for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
  647. Mark[Index].Tag = (UINT8)Index;
  648. Mark[Index].Len = 0;
  649. }
  650. //
  651. // Get list of the options from the seed packet, then put
  652. // them to the mark array according to their tags.
  653. //
  654. SeedOptions = NULL;
  655. Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
  656. if (EFI_ERROR (Status)) {
  657. goto ON_ERROR;
  658. }
  659. if (SeedOptions != NULL) {
  660. for (Index = 0; Index < (UINT32)Count; Index++) {
  661. Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
  662. }
  663. }
  664. //
  665. // Mark the option's length is zero if it is in the DeleteList.
  666. //
  667. for (Index = 0; Index < DeleteCount; Index++) {
  668. Mark[DeleteList[Index]].Len = 0;
  669. }
  670. //
  671. // Add or replace the option if it is in the append list.
  672. //
  673. for (Index = 0; Index < AppendCount; Index++) {
  674. Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length;
  675. Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
  676. }
  677. //
  678. // compute the new packet length. No need to add 1 byte for
  679. // EOP option since EFI_DHCP4_PACKET includes one extra byte
  680. // for option. It is necessary to split the option if it is
  681. // longer than 255 bytes.
  682. //
  683. Len = sizeof (EFI_DHCP4_PACKET);
  684. for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
  685. if (Mark[Index].Len != 0) {
  686. Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
  687. }
  688. }
  689. Status = EFI_OUT_OF_RESOURCES;
  690. Packet = (EFI_DHCP4_PACKET *)AllocatePool (Len);
  691. if (Packet == NULL) {
  692. goto ON_ERROR;
  693. }
  694. Packet->Size = Len;
  695. Packet->Length = 0;
  696. CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));
  697. Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
  698. Buf = Packet->Dhcp4.Option;
  699. for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
  700. if (Mark[Index].Len != 0) {
  701. Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
  702. }
  703. }
  704. *(Buf++) = DHCP4_TAG_EOP;
  705. Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
  706. + (UINT32)(Buf - Packet->Dhcp4.Option);
  707. *NewPacket = Packet;
  708. Status = EFI_SUCCESS;
  709. ON_ERROR:
  710. if (SeedOptions != NULL) {
  711. FreePool (SeedOptions);
  712. }
  713. FreePool (Mark);
  714. return Status;
  715. }