3cb7f3f4bec6d5a84133aa7187ab35f344c96cda
[mirror_edk2.git] / EdkModulePkg / Universal / Network / PxeDhcp4 / Dxe / PxeDhcp4InitSelect.c
1 /*++
2
3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13 PxeDhcp4InitSelect.c
14
15 Abstract:
16
17 --*/
18
19
20 #include "PxeDhcp4.h"
21
22 #define DebugPrint(x)
23 //
24 // #define DebugPrint(x) Aprint x
25 //
26 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
27 STATIC
28 INTN
29 offer_verify (
30 IN PXE_DHCP4_PRIVATE_DATA *Private,
31 IN DHCP4_PACKET *tx_pkt,
32 IN DHCP4_PACKET *rx_pkt,
33 IN UINTN rx_pkt_size
34 )
35 /*++
36 -2 = ignore, stop waiting
37 -1 = ignore, keep waiting
38 0 = accept, keep waiting
39 1 = accept, stop waiting
40 --*/
41 {
42 EFI_STATUS EfiStatus;
43 DHCP4_PACKET *tmp;
44 DHCP4_OP *msg_type_op;
45 DHCP4_OP *srvid_op;
46 UINT32 magik;
47
48 //
49 // Verify parameters. Touch unused parameters to keep
50 // compiler happy.
51 //
52 ASSERT (Private);
53 ASSERT (rx_pkt);
54
55 if (Private == NULL || rx_pkt == NULL) {
56 return -2;
57 }
58
59 tx_pkt = tx_pkt;
60 rx_pkt_size = rx_pkt_size;
61
62 //
63 // This may be a BOOTP Reply or DHCP Offer packet.
64 // If there is no DHCP magik number, assume that
65 // this is a BOOTP Reply packet.
66 //
67 magik = htonl (DHCP4_MAGIK_NUMBER);
68
69 while (!CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
70 //
71 // If there is no DHCP message type option, assume
72 // this is a BOOTP reply packet and cache it.
73 //
74 EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
75
76 if (EFI_ERROR (EfiStatus)) {
77 break;
78 }
79 //
80 // If there is a DHCP message type option, it must be a
81 // DHCP offer packet
82 //
83 if (msg_type_op->len != 1) {
84 return -1;
85 }
86
87 if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
88 return -1;
89 }
90 //
91 // There must be a server identifier option.
92 //
93 EfiStatus = find_opt (
94 rx_pkt,
95 DHCP4_SERVER_IDENTIFIER,
96 0,
97 &srvid_op
98 );
99
100 if (EFI_ERROR (EfiStatus)) {
101 return -1;
102 }
103
104 if (srvid_op->len != 4) {
105 return -1;
106 }
107 //
108 // Good DHCP offer packet.
109 //
110 break;
111 }
112 //
113 // Good DHCP (or BOOTP) packet. Cache it!
114 //
115 EfiStatus = gBS->AllocatePool (
116 EfiBootServicesData,
117 (Private->offers + 1) * sizeof (DHCP4_PACKET),
118 (VOID **) &tmp
119 );
120
121 if (EFI_ERROR (EfiStatus)) {
122 return -2;
123 }
124
125 ASSERT (tmp);
126
127 if (Private->offers != 0) {
128 CopyMem (
129 tmp,
130 Private->offer_list,
131 Private->offers * sizeof (DHCP4_PACKET)
132 );
133
134 gBS->FreePool (Private->offer_list);
135 }
136
137 CopyMem (&tmp[Private->offers++], rx_pkt, sizeof (DHCP4_PACKET));
138
139 Private->offer_list = tmp;
140
141 return 0;
142 }
143
144 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
145 STATIC
146 INTN
147 acknak_verify (
148 IN PXE_DHCP4_PRIVATE_DATA *Private,
149 IN DHCP4_PACKET *tx_pkt,
150 IN DHCP4_PACKET *rx_pkt,
151 IN UINTN rx_pkt_size
152 )
153 /*++
154 -2 = ignore, stop waiting
155 -1 = ignore, keep waiting
156 0 = accept, keep waiting
157 1 = accept, stop waiting
158 --*/
159 {
160 EFI_STATUS EfiStatus;
161 DHCP4_OP *msg_type_op;
162 DHCP4_OP *srvid_op;
163 DHCP4_OP *renew_op;
164 DHCP4_OP *rebind_op;
165 DHCP4_OP *lease_time_op;
166 UINT32 magik;
167
168 //
169 // Verify parameters. Touch unused parameters to
170 // keep compiler happy.
171 //
172 ASSERT (Private);
173 ASSERT (rx_pkt);
174
175 if (Private == NULL || rx_pkt == NULL) {
176 return -2;
177 }
178
179 tx_pkt = tx_pkt;
180 rx_pkt_size = rx_pkt_size;
181
182 //
183 // This must be a DHCP Ack message.
184 //
185 magik = htonl (DHCP4_MAGIK_NUMBER);
186
187 if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
188 return -1;
189 }
190
191 EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
192
193 if (EFI_ERROR (EfiStatus)) {
194 return -1;
195 }
196
197 if (msg_type_op->len != 1) {
198 return -1;
199 }
200
201 if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {
202 return -1;
203 }
204 //
205 // There must be a server identifier.
206 //
207 EfiStatus = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);
208
209 if (EFI_ERROR (EfiStatus)) {
210 return -1;
211 }
212
213 if (srvid_op->len != 4) {
214 return -1;
215 }
216 //
217 // There should be a renewal time.
218 // If there is not, we will default to the 7/8 of the rebinding time.
219 //
220 EfiStatus = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);
221
222 if (EFI_ERROR (EfiStatus)) {
223 renew_op = NULL;
224 } else if (renew_op->len != 4) {
225 renew_op = NULL;
226 }
227 //
228 // There should be a rebinding time.
229 // If there is not, we will default to 7/8 of the lease time.
230 //
231 EfiStatus = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);
232
233 if (EFI_ERROR (EfiStatus)) {
234 rebind_op = NULL;
235 } else if (rebind_op->len != 4) {
236 rebind_op = NULL;
237 }
238 //
239 // There should be a lease time.
240 // If there is not, we will default to one week.
241 //
242 EfiStatus = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);
243
244 if (EFI_ERROR (EfiStatus)) {
245 lease_time_op = NULL;
246 } else if (lease_time_op->len != 4) {
247 lease_time_op = NULL;
248 }
249 //
250 // Packet looks good. Double check the renew, rebind and lease times.
251 //
252 CopyMem (&Private->ServerIp, srvid_op->data, 4);
253
254 if (renew_op != NULL) {
255 CopyMem (&Private->RenewTime, renew_op->data, 4);
256 Private->RenewTime = htonl (Private->RenewTime);
257 } else {
258 Private->RenewTime = 0;
259 }
260
261 if (rebind_op != NULL) {
262 CopyMem (&Private->RebindTime, rebind_op->data, 4);
263 Private->RebindTime = htonl (Private->RebindTime);
264 } else {
265 Private->RebindTime = 0;
266 }
267
268 if (lease_time_op != NULL) {
269 CopyMem (&Private->LeaseTime, lease_time_op->data, 4);
270 Private->LeaseTime = htonl (Private->LeaseTime);
271 } else {
272 Private->LeaseTime = 0;
273 }
274
275 if (Private->LeaseTime < 60) {
276 Private->LeaseTime = 7 * 86400;
277 }
278
279 if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {
280 Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;
281 }
282
283 if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {
284 Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;
285 }
286
287 return 1;
288 }
289
290 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
291 EFI_STATUS
292 EFIAPI
293 PxeDhcp4Init (
294 IN EFI_PXE_DHCP4_PROTOCOL *This,
295 IN UINTN seconds_timeout,
296 OUT UINTN *Offers,
297 OUT DHCP4_PACKET **OfferList
298 )
299 {
300 PXE_DHCP4_PRIVATE_DATA *Private;
301 DHCP4_PACKET offer;
302 EFI_IP_ADDRESS bcast_ip;
303 EFI_STATUS EfiStatus;
304
305 //
306 // Verify parameters and protocol state.
307 //
308 if (This == NULL ||
309 seconds_timeout < DHCP4_MIN_SECONDS ||
310 seconds_timeout > DHCP4_MAX_SECONDS ||
311 Offers == NULL ||
312 OfferList == NULL
313 ) {
314 //
315 // Return parameters are not initialized when
316 // parameters are invalid!
317 //
318 return EFI_INVALID_PARAMETER;
319 }
320
321 *Offers = 0;
322 *OfferList = NULL;
323
324 //
325 // Check protocol state.
326 //
327 if (This->Data == NULL) {
328 return EFI_NOT_STARTED;
329 }
330
331 if (!This->Data->SetupCompleted) {
332 return EFI_NOT_READY;
333 }
334
335 //
336 // Get pointer to our instance data.
337 //
338 Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
339
340 if (Private == NULL) {
341 return EFI_INVALID_PARAMETER;
342 }
343
344 if (Private->PxeBc == NULL) {
345 return EFI_DEVICE_ERROR;
346 }
347 //
348 // Setup variables...
349 //
350 Private->offers = 0;
351 Private->offer_list = NULL;
352
353 EfiStatus = gBS->HandleProtocol (
354 Private->Handle,
355 &gEfiPxeDhcp4CallbackProtocolGuid,
356 (VOID *) &Private->callback
357 );
358
359 if (EFI_ERROR (EfiStatus)) {
360 Private->callback = NULL;
361 }
362
363 Private->function = EFI_PXE_DHCP4_FUNCTION_INIT;
364
365 //
366 // Increment the transaction ID.
367 //
368 {
369 UINT32 xid;
370
371 CopyMem (&xid, &This->Data->Discover.dhcp4.xid, sizeof (UINT32));
372
373 xid = htonl (htonl (xid) + 1);
374
375 CopyMem (&This->Data->Discover.dhcp4.xid, &xid, sizeof (UINT32));
376 }
377 //
378 // Transmit discover and wait for offers...
379 //
380 SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
381
382 EfiStatus = tx_rx_udp (
383 Private,
384 &bcast_ip,
385 NULL,
386 NULL,
387 NULL,
388 &This->Data->Discover,
389 &offer,
390 &offer_verify,
391 seconds_timeout
392 );
393
394 if (EFI_ERROR (EfiStatus)) {
395 if (Private->offer_list) {
396 gBS->FreePool (Private->offer_list);
397 }
398
399 Private->offers = 0;
400 Private->offer_list = NULL;
401 Private->callback = NULL;
402
403 DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
404 return EfiStatus;
405 }
406
407 *Offers = Private->offers;
408 *OfferList = Private->offer_list;
409
410 Private->offers = 0;
411 Private->offer_list = NULL;
412 Private->callback = NULL;
413
414 This->Data->InitCompleted = TRUE;
415 This->Data->SelectCompleted = FALSE;
416 This->Data->IsBootp = FALSE;
417 This->Data->IsAck = FALSE;
418
419 return EFI_SUCCESS;
420 }
421
422 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
423 EFI_STATUS
424 EFIAPI
425 PxeDhcp4Select (
426 IN EFI_PXE_DHCP4_PROTOCOL *This,
427 IN UINTN seconds_timeout,
428 IN DHCP4_PACKET *Offer
429 )
430 {
431 PXE_DHCP4_PRIVATE_DATA *Private;
432 EFI_STATUS EfiStatus;
433 DHCP4_PACKET request;
434 DHCP4_PACKET acknak;
435 EFI_IP_ADDRESS bcast_ip;
436 EFI_IP_ADDRESS zero_ip;
437 EFI_IP_ADDRESS local_ip;
438 DHCP4_OP *srvid;
439 DHCP4_OP *op;
440 UINT32 dhcp4_magik;
441 UINT8 buf[16];
442 BOOLEAN is_bootp;
443
444 //
445 // Verify parameters.
446 //
447 if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS || Offer == NULL) {
448 return EFI_INVALID_PARAMETER;
449 }
450 //
451 // Check protocol state.
452 //
453 if (This->Data == NULL) {
454 return EFI_NOT_STARTED;
455 }
456
457 if (!This->Data->SetupCompleted) {
458 return EFI_NOT_READY;
459 }
460 //
461 // Get pointer to instance data.
462 //
463 Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
464
465 if (Private == NULL) {
466 return EFI_INVALID_PARAMETER;
467 }
468
469 if (Private->PxeBc == NULL) {
470 return EFI_DEVICE_ERROR;
471 }
472
473 //
474 // Setup useful variables...
475 //
476 SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
477
478 ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
479
480 ZeroMem (&local_ip, sizeof (EFI_IP_ADDRESS));
481 local_ip.v4.Addr[0] = 127;
482 local_ip.v4.Addr[3] = 1;
483
484 This->Data->SelectCompleted = FALSE;
485 This->Data->IsBootp = FALSE;
486 This->Data->IsAck = FALSE;
487
488 EfiStatus = gBS->HandleProtocol (
489 Private->Handle,
490 &gEfiPxeDhcp4CallbackProtocolGuid,
491 (VOID *) &Private->callback
492 );
493
494 if (EFI_ERROR (EfiStatus)) {
495 Private->callback = NULL;
496 }
497
498 Private->function = EFI_PXE_DHCP4_FUNCTION_SELECT;
499
500 //
501 // Verify offer packet fields.
502 //
503 if (Offer->dhcp4.op != BOOTP_REPLY) {
504 Private->callback = NULL;
505 return EFI_INVALID_PARAMETER;
506 }
507
508 if (Offer->dhcp4.htype != This->Data->Discover.dhcp4.htype) {
509 Private->callback = NULL;
510 return EFI_INVALID_PARAMETER;
511 }
512
513 if (Offer->dhcp4.hlen != This->Data->Discover.dhcp4.hlen) {
514 Private->callback = NULL;
515 return EFI_INVALID_PARAMETER;
516 }
517
518 if (CompareMem (&Offer->dhcp4.xid, &This->Data->Discover.dhcp4.xid, 4)) {
519 Private->callback = NULL;
520 return EFI_INVALID_PARAMETER;
521 }
522
523 if (!CompareMem (&Offer->dhcp4.yiaddr, &bcast_ip, 4)) {
524 Private->callback = NULL;
525 return EFI_INVALID_PARAMETER;
526 }
527
528 if (!CompareMem (&Offer->dhcp4.yiaddr, &zero_ip, 4)) {
529 Private->callback = NULL;
530 return EFI_INVALID_PARAMETER;
531 }
532
533 if (!CompareMem (&Offer->dhcp4.yiaddr, &local_ip, 4)) {
534 Private->callback = NULL;
535 return EFI_INVALID_PARAMETER;
536 }
537
538 if (CompareMem (
539 &Offer->dhcp4.chaddr,
540 &This->Data->Discover.dhcp4.chaddr,
541 16
542 )) {
543 Private->callback = NULL;
544 return EFI_INVALID_PARAMETER;
545 }
546 //
547 // DHCP option checks
548 //
549 dhcp4_magik = htonl (DHCP4_MAGIK_NUMBER);
550 is_bootp = TRUE;
551
552 if (!CompareMem (&Offer->dhcp4.magik, &dhcp4_magik, 4)) {
553 //
554 // If present, DHCP message type must be offer.
555 //
556 EfiStatus = find_opt (Offer, DHCP4_MESSAGE_TYPE, 0, &op);
557
558 if (!EFI_ERROR (EfiStatus)) {
559 if (op->len != 1 || op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
560 Private->callback = NULL;
561 return EFI_INVALID_PARAMETER;
562 }
563
564 is_bootp = FALSE;
565 }
566 //
567 // If present, DHCP max message size must be valid.
568 //
569 EfiStatus = find_opt (Offer, DHCP4_MAX_MESSAGE_SIZE, 0, &op);
570
571 if (!EFI_ERROR (EfiStatus)) {
572 if (op->len != 2 || ((op->data[0] << 8) | op->data[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {
573 Private->callback = NULL;
574 return EFI_INVALID_PARAMETER;
575 }
576 }
577 //
578 // If present, DHCP server identifier must be valid.
579 //
580 EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &op);
581
582 if (!EFI_ERROR (EfiStatus)) {
583 if (op->len != 4 || !CompareMem (op->data, &bcast_ip, 4) || !CompareMem (op->data, &zero_ip, 4)) {
584 Private->callback = NULL;
585 return EFI_INVALID_PARAMETER;
586 }
587 }
588 //
589 // If present, DHCP subnet mask must be valid.
590 //
591 EfiStatus = find_opt (
592 Offer,
593 DHCP4_SUBNET_MASK,
594 0,
595 &op
596 );
597
598 if (!EFI_ERROR (EfiStatus)) {
599 if (op->len != 4) {
600 Private->callback = NULL;
601 return EFI_INVALID_PARAMETER;
602 }
603 }
604 }
605 //
606 // Early out for BOOTP.
607 //
608 This->Data->IsBootp = is_bootp;
609 if (is_bootp) {
610 //
611 // Copy offer packet to instance data.
612 //
613 CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
614
615 //
616 // Copy discover to request and offer to acknak.
617 //
618 CopyMem (
619 &This->Data->Request,
620 &This->Data->Discover,
621 sizeof (DHCP4_PACKET)
622 );
623
624 CopyMem (
625 &This->Data->AckNak,
626 &This->Data->Offer,
627 sizeof (DHCP4_PACKET)
628 );
629
630 //
631 // Set state flags.
632 //
633 This->Data->SelectCompleted = TRUE;
634 This->Data->IsAck = TRUE;
635
636 Private->callback = NULL;
637 return EFI_SUCCESS;
638 }
639 //
640 // Copy discover packet contents to request packet.
641 //
642 CopyMem (&request, &This->Data->Discover, sizeof (DHCP4_PACKET));
643
644 This->Data->IsAck = FALSE;
645
646 //
647 // Change DHCP message type from discover to request.
648 //
649 EfiStatus = find_opt (&request, DHCP4_MESSAGE_TYPE, 0, &op);
650
651 if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) {
652 Private->callback = NULL;
653 return EFI_INVALID_PARAMETER;
654 }
655
656 if (EfiStatus == EFI_NOT_FOUND) {
657 EfiStatus = find_opt (&request, DHCP4_END, 0, &op);
658
659 if (EFI_ERROR (EfiStatus)) {
660 Private->callback = NULL;
661 return EFI_INVALID_PARAMETER;
662 }
663
664 op->op = DHCP4_MESSAGE_TYPE;
665 op->len = 1;
666
667 op->data[1] = DHCP4_END;
668 }
669
670 op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;
671
672 //
673 // Copy server identifier option from offer to request.
674 //
675 EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &srvid);
676
677 if (EFI_ERROR (EfiStatus)) {
678 Private->callback = NULL;
679 return EFI_INVALID_PARAMETER;
680 }
681
682 if (srvid->len != 4) {
683 Private->callback = NULL;
684 return EFI_INVALID_PARAMETER;
685 }
686
687 EfiStatus = add_opt (&request, srvid);
688
689 if (EFI_ERROR (EfiStatus)) {
690 DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
691 Private->callback = NULL;
692 return EfiStatus;
693 }
694 //
695 // Add requested IP address option to request packet.
696 //
697 op = (DHCP4_OP *) buf;
698 op->op = DHCP4_REQUESTED_IP_ADDRESS;
699 op->len = 4;
700 CopyMem (op->data, &Offer->dhcp4.yiaddr, 4);
701
702 EfiStatus = add_opt (&request, op);
703
704 if (EFI_ERROR (EfiStatus)) {
705 DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
706 Private->callback = NULL;
707 return EfiStatus;
708 }
709 //
710 // Transimit DHCP request and wait for DHCP ack...
711 //
712 SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
713
714 EfiStatus = tx_rx_udp (
715 Private,
716 &bcast_ip,
717 NULL,
718 NULL,
719 NULL,
720 &request,
721 &acknak,
722 &acknak_verify,
723 seconds_timeout
724 );
725
726 if (EFI_ERROR (EfiStatus)) {
727 DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
728 Private->callback = NULL;
729 return EfiStatus;
730 }
731 //
732 // Set Data->IsAck and return.
733 //
734 EfiStatus = find_opt (&acknak, DHCP4_MESSAGE_TYPE, 0, &op);
735
736 if (EFI_ERROR (EfiStatus)) {
737 Private->callback = NULL;
738 return EFI_DEVICE_ERROR;
739 }
740
741 if (op->len != 1) {
742 Private->callback = NULL;
743 return EFI_DEVICE_ERROR;
744 }
745
746 switch (op->data[0]) {
747 case DHCP4_MESSAGE_TYPE_ACK:
748 This->Data->IsAck = TRUE;
749 break;
750
751 case DHCP4_MESSAGE_TYPE_NAK:
752 This->Data->IsAck = FALSE;
753 break;
754
755 default:
756 Private->callback = NULL;
757 return EFI_DEVICE_ERROR;
758 }
759 //
760 // Copy packets into instance data...
761 //
762 CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
763 CopyMem (&This->Data->Request, &request, sizeof (DHCP4_PACKET));
764 CopyMem (&This->Data->AckNak, &acknak, sizeof (DHCP4_PACKET));
765
766 This->Data->SelectCompleted = TRUE;
767
768 Private->callback = NULL;
769 return EFI_SUCCESS;
770 }
771
772 /* eof - PxeDhcp4InitSelect.c */