]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Ip6Dxe/Ip6Option.c
4d92a852dc864757aa8d3f4b05acaa58740d0c3e
[mirror_edk2.git] / NetworkPkg / Ip6Dxe / Ip6Option.c
1 /** @file
2 IP6 option support functions and routines.
3
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "Ip6Impl.h"
11
12 /**
13 Validate the IP6 option format for both the packets we received
14 and that we will transmit. It will compute the ICMPv6 error message fields
15 if the option is malformatted.
16
17 @param[in] IpSb The IP6 service data.
18 @param[in] Packet The to be validated packet.
19 @param[in] Option The first byte of the option.
20 @param[in] OptionLen The length of the whole option.
21 @param[in] Pointer Identifies the octet offset within
22 the invoking packet where the error was detected.
23
24
25 @retval TRUE The option is properly formatted.
26 @retval FALSE The option is malformatted.
27
28 **/
29 BOOLEAN
30 Ip6IsOptionValid (
31 IN IP6_SERVICE *IpSb,
32 IN NET_BUF *Packet,
33 IN UINT8 *Option,
34 IN UINT8 OptionLen,
35 IN UINT32 Pointer
36 )
37 {
38 UINT8 Offset;
39 UINT8 OptionType;
40
41 Offset = 0;
42
43 while (Offset < OptionLen) {
44 OptionType = *(Option + Offset);
45
46 switch (OptionType) {
47 case Ip6OptionPad1:
48 //
49 // It is a Pad1 option
50 //
51 Offset++;
52 break;
53 case Ip6OptionPadN:
54 //
55 // It is a PadN option
56 //
57 Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);
58 break;
59 case Ip6OptionRouterAlert:
60 //
61 // It is a Router Alert Option
62 //
63 Offset += 4;
64 break;
65 default:
66 //
67 // The highest-order two bits specify the action must be taken if
68 // the processing IPv6 node does not recognize the option type.
69 //
70 switch (OptionType & Ip6OptionMask) {
71 case Ip6OptionSkip:
72 Offset = (UINT8) (Offset + *(Option + Offset + 1));
73 break;
74 case Ip6OptionDiscard:
75 return FALSE;
76 case Ip6OptionParameterProblem:
77 Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
78 Ip6SendIcmpError (
79 IpSb,
80 Packet,
81 NULL,
82 &Packet->Ip.Ip6->SourceAddress,
83 ICMP_V6_PARAMETER_PROBLEM,
84 2,
85 &Pointer
86 );
87 return FALSE;
88 case Ip6OptionMask:
89 if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
90 Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
91 Ip6SendIcmpError (
92 IpSb,
93 Packet,
94 NULL,
95 &Packet->Ip.Ip6->SourceAddress,
96 ICMP_V6_PARAMETER_PROBLEM,
97 2,
98 &Pointer
99 );
100 }
101
102 return FALSE;
103 break;
104 }
105
106 break;
107 }
108
109 }
110
111 return TRUE;
112 }
113
114 /**
115 Validate the IP6 option format for both the packets we received
116 and that we will transmit. It supports the defined options in Neighbor
117 Discovery messages.
118
119 @param[in] Option The first byte of the option.
120 @param[in] OptionLen The length of the whole option.
121
122 @retval TRUE The option is properly formatted.
123 @retval FALSE The option is malformatted.
124
125 **/
126 BOOLEAN
127 Ip6IsNDOptionValid (
128 IN UINT8 *Option,
129 IN UINT16 OptionLen
130 )
131 {
132 UINT16 Offset;
133 UINT8 OptionType;
134 UINT16 Length;
135
136 Offset = 0;
137
138 while (Offset < OptionLen) {
139 OptionType = *(Option + Offset);
140 Length = (UINT16) (*(Option + Offset + 1) * 8);
141
142 switch (OptionType) {
143 case Ip6OptionPrefixInfo:
144 if (Length != 32) {
145 return FALSE;
146 }
147
148 break;
149
150 case Ip6OptionMtu:
151 if (Length != 8) {
152 return FALSE;
153 }
154
155 break;
156
157 default:
158 //
159 // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and
160 // Ip6OptionRedirected here. For unrecognized options, silently ignore
161 // and continue processing the message.
162 //
163 if (Length == 0) {
164 return FALSE;
165 }
166
167 break;
168 }
169
170 Offset = (UINT16) (Offset + Length);
171 }
172
173 return TRUE;
174 }
175
176
177 /**
178 Validate whether the NextHeader is a known valid protocol or one of the user configured
179 protocols from the upper layer.
180
181 @param[in] IpSb The IP6 service instance.
182 @param[in] NextHeader The next header field.
183
184 @retval TRUE The NextHeader is a known valid protocol or user configured.
185 @retval FALSE The NextHeader is not a known valid protocol.
186
187 **/
188 BOOLEAN
189 Ip6IsValidProtocol (
190 IN IP6_SERVICE *IpSb,
191 IN UINT8 NextHeader
192 )
193 {
194 LIST_ENTRY *Entry;
195 IP6_PROTOCOL *IpInstance;
196
197 if (NextHeader == EFI_IP_PROTO_TCP ||
198 NextHeader == EFI_IP_PROTO_UDP ||
199 NextHeader == IP6_ICMP ||
200 NextHeader == IP6_ESP
201 ) {
202 return TRUE;
203 }
204
205 if (IpSb == NULL) {
206 return FALSE;
207 }
208
209 if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {
210 return FALSE;
211 }
212
213 NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
214 IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
215 if (IpInstance->State == IP6_STATE_CONFIGED) {
216 if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {
217 return TRUE;
218 }
219 }
220 }
221
222 return FALSE;
223 }
224
225 /**
226 Validate the IP6 extension header format for both the packets we received
227 and that we will transmit. It will compute the ICMPv6 error message fields
228 if the option is mal-formatted.
229
230 @param[in] IpSb The IP6 service instance. This is an optional parameter.
231 @param[in] Packet The data of the packet. Ignored if NULL.
232 @param[in] NextHeader The next header field in IPv6 basic header.
233 @param[in] ExtHdrs The first byte of the option.
234 @param[in] ExtHdrsLen The length of the whole option.
235 @param[in] Rcvd The option is from the packet we received if TRUE,
236 otherwise, the option we want to transmit.
237 @param[out] FormerHeader The offset of NextHeader which points to Fragment
238 Header when we received, of the ExtHdrs.
239 Ignored if we transmit.
240 @param[out] LastHeader The pointer of NextHeader of the last extension
241 header processed by IP6.
242 @param[out] RealExtsLen The length of extension headers processed by IP6 layer.
243 This is an optional parameter that may be NULL.
244 @param[out] UnFragmentLen The length of unfragmented length of extension headers.
245 This is an optional parameter that may be NULL.
246 @param[out] Fragmented Indicate whether the packet is fragmented.
247 This is an optional parameter that may be NULL.
248
249 @retval TRUE The option is properly formatted.
250 @retval FALSE The option is malformatted.
251
252 **/
253 BOOLEAN
254 Ip6IsExtsValid (
255 IN IP6_SERVICE *IpSb OPTIONAL,
256 IN NET_BUF *Packet OPTIONAL,
257 IN UINT8 *NextHeader,
258 IN UINT8 *ExtHdrs,
259 IN UINT32 ExtHdrsLen,
260 IN BOOLEAN Rcvd,
261 OUT UINT32 *FormerHeader OPTIONAL,
262 OUT UINT8 **LastHeader,
263 OUT UINT32 *RealExtsLen OPTIONAL,
264 OUT UINT32 *UnFragmentLen OPTIONAL,
265 OUT BOOLEAN *Fragmented OPTIONAL
266 )
267 {
268 UINT32 Pointer;
269 UINT32 Offset;
270 UINT8 *Option;
271 UINT8 OptionLen;
272 BOOLEAN Flag;
273 UINT8 CountD;
274 UINT8 CountA;
275 IP6_FRAGMENT_HEADER *FragmentHead;
276 UINT16 FragmentOffset;
277 IP6_ROUTING_HEADER *RoutingHead;
278
279 if (RealExtsLen != NULL) {
280 *RealExtsLen = 0;
281 }
282
283 if (UnFragmentLen != NULL) {
284 *UnFragmentLen = 0;
285 }
286
287 if (Fragmented != NULL) {
288 *Fragmented = FALSE;
289 }
290
291 *LastHeader = NextHeader;
292
293 if (ExtHdrs == NULL && ExtHdrsLen == 0) {
294 return TRUE;
295 }
296
297 if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
298 return FALSE;
299 }
300
301 Pointer = 0;
302 Offset = 0;
303 Flag = FALSE;
304 CountD = 0;
305 CountA = 0;
306
307 while (Offset <= ExtHdrsLen) {
308
309 switch (*NextHeader) {
310 case IP6_HOP_BY_HOP:
311 if (Offset != 0) {
312 if (!Rcvd) {
313 return FALSE;
314 }
315 //
316 // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.
317 // If not, generate a ICMP parameter problem message with code value of 1.
318 //
319 if (Pointer == 0) {
320 Pointer = sizeof (EFI_IP6_HEADER);
321 } else {
322 Pointer = Offset + sizeof (EFI_IP6_HEADER);
323 }
324
325 if ((IpSb != NULL) && (Packet != NULL) &&
326 !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
327 Ip6SendIcmpError (
328 IpSb,
329 Packet,
330 NULL,
331 &Packet->Ip.Ip6->SourceAddress,
332 ICMP_V6_PARAMETER_PROBLEM,
333 1,
334 &Pointer
335 );
336 }
337 return FALSE;
338 }
339
340 Flag = TRUE;
341
342 //
343 // Fall through
344 //
345 case IP6_DESTINATION:
346 if (*NextHeader == IP6_DESTINATION) {
347 CountD++;
348 }
349
350 if (CountD > 2) {
351 return FALSE;
352 }
353
354 NextHeader = ExtHdrs + Offset;
355 Pointer = Offset;
356
357 Offset++;
358 Option = ExtHdrs + Offset;
359 OptionLen = (UINT8) ((*Option + 1) * 8 - 2);
360 Option++;
361 Offset++;
362
363 if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {
364 return FALSE;
365 }
366
367 Offset = Offset + OptionLen;
368
369 if (Flag) {
370 if (UnFragmentLen != NULL) {
371 *UnFragmentLen = Offset;
372 }
373
374 Flag = FALSE;
375 }
376
377 break;
378
379 case IP6_ROUTING:
380 NextHeader = ExtHdrs + Offset;
381 RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;
382
383 //
384 // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.
385 // Thus all routing types are processed as unrecognized.
386 //
387 if (RoutingHead->SegmentsLeft == 0) {
388 //
389 // Ignore the routing header and proceed to process the next header.
390 //
391 Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;
392
393 if (UnFragmentLen != NULL) {
394 *UnFragmentLen = Offset;
395 }
396
397 } else {
398 //
399 // Discard the packet and send an ICMP Parameter Problem, Code 0, message
400 // to the packet's source address, pointing to the unrecognized routing
401 // type.
402 //
403 Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);
404 if ((IpSb != NULL) && (Packet != NULL) &&
405 !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
406 Ip6SendIcmpError (
407 IpSb,
408 Packet,
409 NULL,
410 &Packet->Ip.Ip6->SourceAddress,
411 ICMP_V6_PARAMETER_PROBLEM,
412 0,
413 &Pointer
414 );
415 }
416
417 return FALSE;
418 }
419
420 break;
421
422 case IP6_FRAGMENT:
423
424 //
425 // RFC2402, AH header should after fragment header.
426 //
427 if (CountA > 1) {
428 return FALSE;
429 }
430
431 //
432 // RFC2460, ICMP Parameter Problem message with code 0 should be sent
433 // if the length of a fragment is not a multiple of 8 octets and the M
434 // flag of that fragment is 1, pointing to the Payload length field of the
435 // fragment packet.
436 //
437 if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {
438 //
439 // Check whether it is the last fragment.
440 //
441 FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);
442 if (FragmentHead == NULL) {
443 return FALSE;
444 }
445
446 FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
447
448 if (((FragmentOffset & 0x1) == 0x1) &&
449 !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
450 Pointer = sizeof (UINT32);
451 Ip6SendIcmpError (
452 IpSb,
453 Packet,
454 NULL,
455 &Packet->Ip.Ip6->SourceAddress,
456 ICMP_V6_PARAMETER_PROBLEM,
457 0,
458 &Pointer
459 );
460 return FALSE;
461 }
462 }
463
464 if (Fragmented != NULL) {
465 *Fragmented = TRUE;
466 }
467
468 if (Rcvd && FormerHeader != NULL) {
469 *FormerHeader = (UINT32) (NextHeader - ExtHdrs);
470 }
471
472 NextHeader = ExtHdrs + Offset;
473 Offset = Offset + 8;
474 break;
475
476 case IP6_AH:
477 if (++CountA > 1) {
478 return FALSE;
479 }
480
481 Option = ExtHdrs + Offset;
482 NextHeader = Option;
483 Option++;
484 //
485 // RFC2402, Payload length is specified in 32-bit words, minus "2".
486 //
487 OptionLen = (UINT8) ((*Option + 2) * 4);
488 Offset = Offset + OptionLen;
489 break;
490
491 case IP6_NO_NEXT_HEADER:
492 *LastHeader = NextHeader;
493 return FALSE;
494 break;
495
496 default:
497 if (Ip6IsValidProtocol (IpSb, *NextHeader)) {
498
499 *LastHeader = NextHeader;
500
501 if (RealExtsLen != NULL) {
502 *RealExtsLen = Offset;
503 }
504
505 return TRUE;
506 }
507
508 //
509 // The Next Header value is unrecognized by the node, discard the packet and
510 // send an ICMP parameter problem message with code value of 1.
511 //
512 if (Offset == 0) {
513 //
514 // The Next Header directly follows IPv6 basic header.
515 //
516 Pointer = 6;
517 } else {
518 if (Pointer == 0) {
519 Pointer = sizeof (EFI_IP6_HEADER);
520 } else {
521 Pointer = Offset + sizeof (EFI_IP6_HEADER);
522 }
523 }
524
525 if ((IpSb != NULL) && (Packet != NULL) &&
526 !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
527 Ip6SendIcmpError (
528 IpSb,
529 Packet,
530 NULL,
531 &Packet->Ip.Ip6->SourceAddress,
532 ICMP_V6_PARAMETER_PROBLEM,
533 1,
534 &Pointer
535 );
536 }
537 return FALSE;
538 }
539 }
540
541 *LastHeader = NextHeader;
542
543 if (RealExtsLen != NULL) {
544 *RealExtsLen = Offset;
545 }
546
547 return TRUE;
548 }
549
550 /**
551 Generate an IPv6 router alert option in network order and output it through Buffer.
552
553 @param[out] Buffer Points to a buffer to record the generated option.
554 @param[in, out] BufferLen The length of Buffer, in bytes.
555 @param[in] NextHeader The 8-bit selector indicates the type of header
556 immediately following the Hop-by-Hop Options header.
557
558 @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated
559 option. BufferLen is updated for the required size.
560
561 @retval EFI_SUCCESS The option is generated and filled in to Buffer.
562
563 **/
564 EFI_STATUS
565 Ip6FillHopByHop (
566 OUT UINT8 *Buffer,
567 IN OUT UINTN *BufferLen,
568 IN UINT8 NextHeader
569 )
570 {
571 UINT8 BufferArray[8];
572
573 if (*BufferLen < 8) {
574 *BufferLen = 8;
575 return EFI_BUFFER_TOO_SMALL;
576 }
577
578 //
579 // Form the Hop-By-Hop option in network order.
580 // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN
581 // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.
582 //
583 ZeroMem (BufferArray, sizeof (BufferArray));
584 BufferArray[0] = NextHeader;
585 BufferArray[2] = 0x5;
586 BufferArray[3] = 0x2;
587 BufferArray[6] = 1;
588
589 CopyMem (Buffer, BufferArray, sizeof (BufferArray));
590 return EFI_SUCCESS;
591 }
592
593 /**
594 Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.
595
596 @param[in] IpSb The IP6 service instance to transmit the packet.
597 @param[in] NextHeader The extension header type of first extension header.
598 @param[in] LastHeader The extension header type of last extension header.
599 @param[in] ExtHdrs The length of the original extension header.
600 @param[in] ExtHdrsLen The length of the extension headers.
601 @param[in] FragmentOffset The fragment offset of the data following the header.
602 @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.
603 It's caller's responsibility to free this buffer.
604
605 @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of
606 resource.
607 @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not
608 supported currently.
609 @retval EFI_SUCCESS The operation performed successfully.
610
611 **/
612 EFI_STATUS
613 Ip6FillFragmentHeader (
614 IN IP6_SERVICE *IpSb,
615 IN UINT8 NextHeader,
616 IN UINT8 LastHeader,
617 IN UINT8 *ExtHdrs,
618 IN UINT32 ExtHdrsLen,
619 IN UINT16 FragmentOffset,
620 OUT UINT8 **UpdatedExtHdrs
621 )
622 {
623 UINT32 Length;
624 UINT8 *Buffer;
625 UINT32 FormerHeader;
626 UINT32 Offset;
627 UINT32 Part1Len;
628 UINT32 HeaderLen;
629 UINT8 Current;
630 IP6_FRAGMENT_HEADER FragmentHead;
631
632 if (UpdatedExtHdrs == NULL) {
633 return EFI_INVALID_PARAMETER;
634 }
635
636 Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
637 Buffer = AllocatePool (Length);
638 if (Buffer == NULL) {
639 return EFI_OUT_OF_RESOURCES;
640 }
641
642 Offset = 0;
643 Part1Len = 0;
644 FormerHeader = 0;
645 Current = NextHeader;
646
647 while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {
648 switch (NextHeader) {
649 case IP6_ROUTING:
650 case IP6_HOP_BY_HOP:
651 case IP6_DESTINATION:
652 Current = NextHeader;
653 NextHeader = *(ExtHdrs + Offset);
654
655 if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {
656 //
657 // Destination Options header should occur at most twice, once before
658 // a Routing header and once before the upper-layer header. Here we
659 // find the one before the upper-layer header. Insert the Fragment
660 // Header before it.
661 //
662 CopyMem (Buffer, ExtHdrs, Part1Len);
663 *(Buffer + FormerHeader) = IP6_FRAGMENT;
664 //
665 // Exit the loop.
666 //
667 Offset = ExtHdrsLen + 1;
668 break;
669 }
670
671
672 FormerHeader = Offset;
673 HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8;
674 Part1Len = Part1Len + HeaderLen;
675 Offset = Offset + HeaderLen;
676 break;
677
678 case IP6_FRAGMENT:
679 Current = NextHeader;
680
681 if (Part1Len != 0) {
682 CopyMem (Buffer, ExtHdrs, Part1Len);
683 }
684
685 *(Buffer + FormerHeader) = IP6_FRAGMENT;
686
687 //
688 // Exit the loop.
689 //
690 Offset = ExtHdrsLen + 1;
691 break;
692
693 case IP6_AH:
694 Current = NextHeader;
695 NextHeader = *(ExtHdrs + Offset);
696 //
697 // RFC2402, Payload length is specified in 32-bit words, minus "2".
698 //
699 HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4;
700 Part1Len = Part1Len + HeaderLen;
701 Offset = Offset + HeaderLen;
702 break;
703
704 default:
705 if (Ip6IsValidProtocol (IpSb, NextHeader)) {
706 Current = NextHeader;
707 CopyMem (Buffer, ExtHdrs, Part1Len);
708 *(Buffer + FormerHeader) = IP6_FRAGMENT;
709 //
710 // Exit the loop.
711 //
712 Offset = ExtHdrsLen + 1;
713 break;
714 }
715
716 FreePool (Buffer);
717 return EFI_UNSUPPORTED;
718 }
719 }
720
721 //
722 // Append the Fragment header. If the fragment offset indicates the fragment
723 // is the first fragment.
724 //
725 if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
726 FragmentHead.NextHeader = Current;
727 } else {
728 FragmentHead.NextHeader = LastHeader;
729 }
730
731 FragmentHead.Reserved = 0;
732 FragmentHead.FragmentOffset = HTONS (FragmentOffset);
733 FragmentHead.Identification = mIp6Id;
734
735 CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));
736
737 if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {
738 //
739 // Append the part2 (fragmentable part) of Extension headers
740 //
741 CopyMem (
742 Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),
743 ExtHdrs + Part1Len,
744 ExtHdrsLen - Part1Len
745 );
746 }
747
748 *UpdatedExtHdrs = Buffer;
749
750 return EFI_SUCCESS;
751 }
752