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