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