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