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