summaryrefslogtreecommitdiff
path: root/Core/NetworkPkg/Ip6Dxe/Ip6Option.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/NetworkPkg/Ip6Dxe/Ip6Option.c')
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Option.c758
1 files changed, 758 insertions, 0 deletions
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Option.c b/Core/NetworkPkg/Ip6Dxe/Ip6Option.c
new file mode 100644
index 0000000000..9a91fd7cd1
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Option.c
@@ -0,0 +1,758 @@
+/** @file
+ IP6 option support functions and routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Validate the IP6 option format for both the packets we received
+ and that we will transmit. It will compute the ICMPv6 error message fields
+ if the option is malformated.
+
+ @param[in] IpSb The IP6 service data.
+ @param[in] Packet The to be validated packet.
+ @param[in] Option The first byte of the option.
+ @param[in] OptionLen The length of the whole option.
+ @param[in] Pointer Identifies the octet offset within
+ the invoking packet where the error was detected.
+
+
+ @retval TRUE The option is properly formatted.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsOptionValid (
+ IN IP6_SERVICE *IpSb,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT8 OptionLen,
+ IN UINT32 Pointer
+ )
+{
+ UINT8 Offset;
+ UINT8 OptionType;
+
+ Offset = 0;
+
+ while (Offset < OptionLen) {
+ OptionType = *(Option + Offset);
+
+ switch (OptionType) {
+ case Ip6OptionPad1:
+ //
+ // It is a Pad1 option
+ //
+ Offset++;
+ break;
+ case Ip6OptionPadN:
+ //
+ // It is a PadN option
+ //
+ Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);
+ break;
+ case Ip6OptionRouterAlert:
+ //
+ // It is a Router Alert Option
+ //
+ Offset += 4;
+ break;
+ default:
+ //
+ // The highest-order two bits specify the action must be taken if
+ // the processing IPv6 node does not recognize the option type.
+ //
+ switch (OptionType & Ip6OptionMask) {
+ case Ip6OptionSkip:
+ Offset = (UINT8) (Offset + *(Option + Offset + 1));
+ break;
+ case Ip6OptionDiscard:
+ return FALSE;
+ case Ip6OptionParameterProblem:
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 2,
+ &Pointer
+ );
+ return FALSE;
+ case Ip6OptionMask:
+ if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 2,
+ &Pointer
+ );
+ }
+
+ return FALSE;
+ break;
+ }
+
+ break;
+ }
+
+ }
+
+ return TRUE;
+}
+
+/**
+ Validate the IP6 option format for both the packets we received
+ and that we will transmit. It supports the defined options in Neighbor
+ Discovery messages.
+
+ @param[in] Option The first byte of the option.
+ @param[in] OptionLen The length of the whole option.
+
+ @retval TRUE The option is properly formatted.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsNDOptionValid (
+ IN UINT8 *Option,
+ IN UINT16 OptionLen
+ )
+{
+ UINT16 Offset;
+ UINT8 OptionType;
+ UINT16 Length;
+
+ Offset = 0;
+
+ while (Offset < OptionLen) {
+ OptionType = *(Option + Offset);
+ Length = (UINT16) (*(Option + Offset + 1) * 8);
+
+ switch (OptionType) {
+ case Ip6OptionPrefixInfo:
+ if (Length != 32) {
+ return FALSE;
+ }
+
+ break;
+
+ case Ip6OptionMtu:
+ if (Length != 8) {
+ return FALSE;
+ }
+
+ break;
+
+ default:
+ //
+ // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and
+ // Ip6OptionRedirected here. For unrecognized options, silently ignore
+ // and continue processsing the message.
+ //
+ if (Length == 0) {
+ return FALSE;
+ }
+
+ break;
+ }
+
+ Offset = (UINT16) (Offset + Length);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Validate whether the NextHeader is a known valid protocol or one of the user configured
+ protocols from the upper layer.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in] NextHeader The next header field.
+
+ @retval TRUE The NextHeader is a known valid protocol or user configured.
+ @retval FALSE The NextHeader is not a known valid protocol.
+
+**/
+BOOLEAN
+Ip6IsValidProtocol (
+ IN IP6_SERVICE *IpSb,
+ IN UINT8 NextHeader
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_PROTOCOL *IpInstance;
+
+ if (NextHeader == EFI_IP_PROTO_TCP ||
+ NextHeader == EFI_IP_PROTO_UDP ||
+ NextHeader == IP6_ICMP ||
+ NextHeader == IP6_ESP
+ ) {
+ return TRUE;
+ }
+
+ if (IpSb == NULL) {
+ return FALSE;
+ }
+
+ if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {
+ return FALSE;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Validate the IP6 extension header format for both the packets we received
+ and that we will transmit. It will compute the ICMPv6 error message fields
+ if the option is mal-formated.
+
+ @param[in] IpSb The IP6 service instance. This is an optional parameter.
+ @param[in] Packet The data of the packet. Ignored if NULL.
+ @param[in] NextHeader The next header field in IPv6 basic header.
+ @param[in] ExtHdrs The first byte of the option.
+ @param[in] ExtHdrsLen The length of the whole option.
+ @param[in] Rcvd The option is from the packet we received if TRUE,
+ otherwise, the option we want to transmit.
+ @param[out] FormerHeader The offset of NextHeader which points to Fragment
+ Header when we received, of the ExtHdrs.
+ Ignored if we transmit.
+ @param[out] LastHeader The pointer of NextHeader of the last extension
+ header processed by IP6.
+ @param[out] RealExtsLen The length of extension headers processed by IP6 layer.
+ This is an optional parameter that may be NULL.
+ @param[out] UnFragmentLen The length of unfragmented length of extension headers.
+ This is an optional parameter that may be NULL.
+ @param[out] Fragmented Indicate whether the packet is fragmented.
+ This is an optional parameter that may be NULL.
+
+ @retval TRUE The option is properly formated.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsExtsValid (
+ IN IP6_SERVICE *IpSb OPTIONAL,
+ IN NET_BUF *Packet OPTIONAL,
+ IN UINT8 *NextHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN BOOLEAN Rcvd,
+ OUT UINT32 *FormerHeader OPTIONAL,
+ OUT UINT8 **LastHeader,
+ OUT UINT32 *RealExtsLen OPTIONAL,
+ OUT UINT32 *UnFragmentLen OPTIONAL,
+ OUT BOOLEAN *Fragmented OPTIONAL
+ )
+{
+ UINT32 Pointer;
+ UINT32 Offset;
+ UINT8 *Option;
+ UINT8 OptionLen;
+ BOOLEAN Flag;
+ UINT8 CountD;
+ UINT8 CountA;
+ IP6_FRAGMENT_HEADER *FragmentHead;
+ UINT16 FragmentOffset;
+ IP6_ROUTING_HEADER *RoutingHead;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = 0;
+ }
+
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = 0;
+ }
+
+ if (Fragmented != NULL) {
+ *Fragmented = FALSE;
+ }
+
+ *LastHeader = NextHeader;
+
+ if (ExtHdrs == NULL && ExtHdrsLen == 0) {
+ return TRUE;
+ }
+
+ if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
+ return FALSE;
+ }
+
+ Pointer = 0;
+ Offset = 0;
+ Flag = FALSE;
+ CountD = 0;
+ CountA = 0;
+
+ while (Offset <= ExtHdrsLen) {
+
+ switch (*NextHeader) {
+ case IP6_HOP_BY_HOP:
+ if (Offset != 0) {
+ if (!Rcvd) {
+ return FALSE;
+ }
+ //
+ // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.
+ // If not, generate a ICMP parameter problem message with code value of 1.
+ //
+ if (Pointer == 0) {
+ Pointer = sizeof (EFI_IP6_HEADER);
+ } else {
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);
+ }
+
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 1,
+ &Pointer
+ );
+ }
+ return FALSE;
+ }
+
+ Flag = TRUE;
+
+ //
+ // Fall through
+ //
+ case IP6_DESTINATION:
+ if (*NextHeader == IP6_DESTINATION) {
+ CountD++;
+ }
+
+ if (CountD > 2) {
+ return FALSE;
+ }
+
+ NextHeader = ExtHdrs + Offset;
+ Pointer = Offset;
+
+ Offset++;
+ Option = ExtHdrs + Offset;
+ OptionLen = (UINT8) ((*Option + 1) * 8 - 2);
+ Option++;
+ Offset++;
+
+ if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {
+ return FALSE;
+ }
+
+ Offset = Offset + OptionLen;
+
+ if (Flag) {
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = Offset;
+ }
+
+ Flag = FALSE;
+ }
+
+ break;
+
+ case IP6_ROUTING:
+ NextHeader = ExtHdrs + Offset;
+ RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;
+
+ //
+ // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.
+ // Thus all routing types are processed as unrecognized.
+ //
+ if (RoutingHead->SegmentsLeft == 0) {
+ //
+ // Ignore the routing header and proceed to process the next header.
+ //
+ Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;
+
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = Offset;
+ }
+
+ } else {
+ //
+ // Discard the packet and send an ICMP Parameter Problem, Code 0, message
+ // to the packet's source address, pointing to the unrecognized routing
+ // type.
+ //
+ Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 0,
+ &Pointer
+ );
+ }
+
+ return FALSE;
+ }
+
+ break;
+
+ case IP6_FRAGMENT:
+
+ //
+ // RFC2402, AH header should after fragment header.
+ //
+ if (CountA > 1) {
+ return FALSE;
+ }
+
+ //
+ // RFC2460, ICMP Parameter Problem message with code 0 should be sent
+ // if the length of a fragment is not a multiple of 8 octects and the M
+ // flag of that fragment is 1, pointing to the Payload length field of the
+ // fragment packet.
+ //
+ if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {
+ //
+ // Check whether it is the last fragment.
+ //
+ FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);
+ if (FragmentHead == NULL) {
+ return FALSE;
+ }
+
+ FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
+
+ if (((FragmentOffset & 0x1) == 0x1) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Pointer = sizeof (UINT32);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 0,
+ &Pointer
+ );
+ return FALSE;
+ }
+ }
+
+ if (Fragmented != NULL) {
+ *Fragmented = TRUE;
+ }
+
+ if (Rcvd && FormerHeader != NULL) {
+ *FormerHeader = (UINT32) (NextHeader - ExtHdrs);
+ }
+
+ NextHeader = ExtHdrs + Offset;
+ Offset = Offset + 8;
+ break;
+
+ case IP6_AH:
+ if (++CountA > 1) {
+ return FALSE;
+ }
+
+ Option = ExtHdrs + Offset;
+ NextHeader = Option;
+ Option++;
+ //
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".
+ //
+ OptionLen = (UINT8) ((*Option + 2) * 4);
+ Offset = Offset + OptionLen;
+ break;
+
+ case IP6_NO_NEXT_HEADER:
+ *LastHeader = NextHeader;
+ return FALSE;
+ break;
+
+ default:
+ if (Ip6IsValidProtocol (IpSb, *NextHeader)) {
+
+ *LastHeader = NextHeader;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = Offset;
+ }
+
+ return TRUE;
+ }
+
+ //
+ // The Next Header value is unrecognized by the node, discard the packet and
+ // send an ICMP parameter problem message with code value of 1.
+ //
+ if (Offset == 0) {
+ //
+ // The Next Header directly follows IPv6 basic header.
+ //
+ Pointer = 6;
+ } else {
+ if (Pointer == 0) {
+ Pointer = sizeof (EFI_IP6_HEADER);
+ } else {
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);
+ }
+ }
+
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 1,
+ &Pointer
+ );
+ }
+ return FALSE;
+ }
+ }
+
+ *LastHeader = NextHeader;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = Offset;
+ }
+
+ return TRUE;
+}
+
+/**
+ Generate an IPv6 router alert option in network order and output it through Buffer.
+
+ @param[out] Buffer Points to a buffer to record the generated option.
+ @param[in, out] BufferLen The length of Buffer, in bytes.
+ @param[in] NextHeader The 8-bit selector indicates the type of header
+ immediately following the Hop-by-Hop Options header.
+
+ @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated
+ option. BufferLen is updated for the required size.
+
+ @retval EFI_SUCCESS The option is generated and filled in to Buffer.
+
+**/
+EFI_STATUS
+Ip6FillHopByHop (
+ OUT UINT8 *Buffer,
+ IN OUT UINTN *BufferLen,
+ IN UINT8 NextHeader
+ )
+{
+ UINT8 BufferArray[8];
+
+ if (*BufferLen < 8) {
+ *BufferLen = 8;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Form the Hop-By-Hop option in network order.
+ // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN
+ // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.
+ //
+ ZeroMem (BufferArray, sizeof (BufferArray));
+ BufferArray[0] = NextHeader;
+ BufferArray[2] = 0x5;
+ BufferArray[3] = 0x2;
+ BufferArray[6] = 1;
+
+ CopyMem (Buffer, BufferArray, sizeof (BufferArray));
+ return EFI_SUCCESS;
+}
+
+/**
+ Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] NextHeader The extension header type of first extension header.
+ @param[in] LastHeader The extension header type of last extension header.
+ @param[in] ExtHdrs The length of the original extension header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] FragmentOffset The fragment offset of the data following the header.
+ @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.
+ It's caller's responsiblity to free this buffer.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of
+ resource.
+ @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not
+ supported currently.
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+Ip6FillFragmentHeader (
+ IN IP6_SERVICE *IpSb,
+ IN UINT8 NextHeader,
+ IN UINT8 LastHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN UINT16 FragmentOffset,
+ OUT UINT8 **UpdatedExtHdrs
+ )
+{
+ UINT32 Length;
+ UINT8 *Buffer;
+ UINT32 FormerHeader;
+ UINT32 Offset;
+ UINT32 Part1Len;
+ UINT32 HeaderLen;
+ UINT8 Current;
+ IP6_FRAGMENT_HEADER FragmentHead;
+
+ if (UpdatedExtHdrs == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
+ Buffer = AllocatePool (Length);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Offset = 0;
+ Part1Len = 0;
+ FormerHeader = 0;
+ Current = NextHeader;
+
+ while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {
+ switch (NextHeader) {
+ case IP6_ROUTING:
+ case IP6_HOP_BY_HOP:
+ case IP6_DESTINATION:
+ Current = NextHeader;
+ NextHeader = *(ExtHdrs + Offset);
+
+ if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {
+ //
+ // Destination Options header should occur at most twice, once before
+ // a Routing header and once before the upper-layer header. Here we
+ // find the one before the upper-layer header. Insert the Fragment
+ // Header before it.
+ //
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+ }
+
+
+ FormerHeader = Offset;
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8;
+ Part1Len = Part1Len + HeaderLen;
+ Offset = Offset + HeaderLen;
+ break;
+
+ case IP6_FRAGMENT:
+ Current = NextHeader;
+
+ if (Part1Len != 0) {
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ }
+
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+
+ case IP6_AH:
+ Current = NextHeader;
+ NextHeader = *(ExtHdrs + Offset);
+ //
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".
+ //
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4;
+ Part1Len = Part1Len + HeaderLen;
+ Offset = Offset + HeaderLen;
+ break;
+
+ default:
+ if (Ip6IsValidProtocol (IpSb, NextHeader)) {
+ Current = NextHeader;
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+ }
+
+ FreePool (Buffer);
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ //
+ // Append the Fragment header. If the fragment offset indicates the fragment
+ // is the first fragment.
+ //
+ if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
+ FragmentHead.NextHeader = Current;
+ } else {
+ FragmentHead.NextHeader = LastHeader;
+ }
+
+ FragmentHead.Reserved = 0;
+ FragmentHead.FragmentOffset = HTONS (FragmentOffset);
+ FragmentHead.Identification = mIp6Id;
+
+ CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));
+
+ if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {
+ //
+ // Append the part2 (fragmentable part) of Extension headers
+ //
+ CopyMem (
+ Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),
+ ExtHdrs + Part1Len,
+ ExtHdrsLen - Part1Len
+ );
+ }
+
+ *UpdatedExtHdrs = Buffer;
+
+ return EFI_SUCCESS;
+}
+