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