--- /dev/null
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+ Ip4Option.c\r
+\r
+Abstract:\r
+\r
+ IP4 option support functions\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+ Validate the IP4 option format for both the packets we received\r
+ and will transmit. It will compute the ICMP error message fields\r
+ if the option is mal-formated. But this information isn't used.\r
+\r
+ @param Option The first byte of the option\r
+ @param OptionLen The length of the whole option\r
+ @param Rcvd The option is from the packet we received if TRUE,\r
+ otherwise the option we wants to transmit.\r
+\r
+ @return TRUE: The option is properly formated\r
+ @return FALSE: The option is mal-formated\r
+\r
+**/\r
+BOOLEAN\r
+Ip4OptionIsValid (\r
+ IN UINT8 *Option,\r
+ IN UINT32 OptionLen,\r
+ IN BOOLEAN Rcvd\r
+ )\r
+{\r
+ UINT32 Cur;\r
+ UINT32 Len;\r
+ UINT32 Point;\r
+ UINT8 IcmpType;\r
+ UINT8 IcmpCode;\r
+ UINT32 IcmpPoint;\r
+\r
+ IcmpType = ICMP_PARAMETER_PROBLEM;\r
+ IcmpCode = 0;\r
+ IcmpPoint = 0;\r
+\r
+ Cur = 0;\r
+\r
+ while (Cur < OptionLen) {\r
+ switch (Option[Cur]) {\r
+ case IP4_OPTION_NOP:\r
+ Cur++;\r
+ break;\r
+\r
+ case IP4_OPTION_EOP:\r
+ Cur = OptionLen;\r
+ break;\r
+\r
+ case IP4_OPTION_LSRR:\r
+ case IP4_OPTION_SSRR:\r
+ case IP4_OPTION_RR:\r
+ Len = Option[Cur + 1];\r
+ Point = Option[Cur + 2];\r
+\r
+ //\r
+ // SRR/RR options are formated as |Type|Len|Point|Ip1|Ip2|...\r
+ //\r
+ if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) {\r
+ IcmpPoint = Cur + 1;\r
+ return FALSE;\r
+ }\r
+\r
+ if ((Point > Len + 1) || (Point % 4 != 0)) {\r
+ IcmpPoint = Cur + 2;\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // The Point must point pass the last entry if the packet is received\r
+ // by us. It must point to 4 if the packet is to be sent by us for\r
+ // source route option.\r
+ //\r
+ if ((Option[Cur] != IP4_OPTION_RR) &&\r
+ ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) {\r
+\r
+ IcmpType = ICMP_DEST_UNREACHABLE;\r
+ IcmpCode = ICMP_SOURCEROUTE_FAILED;\r
+ return FALSE;\r
+ }\r
+\r
+ Cur += Len;\r
+ break;\r
+\r
+ default:\r
+ Len = Option[Cur + 1];\r
+\r
+ if ((OptionLen - Cur < Len) || (Len < 2)) {\r
+ IcmpPoint = Cur + 1;\r
+ return FALSE;\r
+ }\r
+\r
+ Cur = Cur + Len;\r
+ break;\r
+ }\r
+\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Copy the option from the original option to buffer. It\r
+ handles the details such as:\r
+ 1. whether copy the single IP4 option to the first/non-first\r
+ fragments.\r
+ 2. Pad the options copied over to aligened to 4 bytes.\r
+\r
+ @param Option The original option to copy from\r
+ @param OptionLen The length of the original option\r
+ @param FirstFragment Whether it is the first fragment\r
+ @param Buf The buffer to copy options to\r
+ @param BufLen The length of the buffer\r
+\r
+ @retval EFI_SUCCESS The options are copied over\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4CopyOption (\r
+ IN UINT8 *Option,\r
+ IN UINT32 OptionLen,\r
+ IN BOOLEAN FirstFragment,\r
+ IN UINT8 *Buf, OPTIONAL\r
+ IN OUT UINT32 *BufLen\r
+ )\r
+{\r
+ UINT8 OptBuf[40];\r
+ UINT32 Cur;\r
+ UINT32 Next;\r
+ UINT8 Type;\r
+ UINT32 Len;\r
+\r
+ ASSERT ((BufLen != NULL) && (OptionLen <= 40));\r
+\r
+ Cur = 0;\r
+ Next = 0;\r
+\r
+ while (Cur < OptionLen) {\r
+ Type = Option[Cur];\r
+ Len = Option[Cur + 1];\r
+\r
+ if (Type == IP4_OPTION_NOP) {\r
+ //\r
+ // Keep the padding, in case that the sender wants to align\r
+ // the option, say, to 4 bytes\r
+ //\r
+ OptBuf[Next] = IP4_OPTION_NOP;\r
+ Next++;\r
+ Cur++;\r
+\r
+ } else if (Type == IP4_OPTION_EOP) {\r
+ //\r
+ // Don't append the EOP to avoid including only a EOP option\r
+ //\r
+ break;\r
+\r
+ } else {\r
+ //\r
+ // don't copy options that is only valid for the first fragment\r
+ //\r
+ if (FirstFragment || (Type & IP4_OPTION_COPY_MASK)) {\r
+ NetCopyMem (OptBuf + Next, Option + Cur, Len);\r
+ Next += Len;\r
+ }\r
+\r
+ Cur += Len;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Don't append an EOP only option.\r
+ //\r
+ if (Next == 0) {\r
+ *BufLen = 0;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Append an EOP if the end of option doesn't coincide with the\r
+ // end of the IP header, that is, isn't aligned to 4 bytes..\r
+ //\r
+ if ((Next % 4) != 0) {\r
+ OptBuf[Next] = IP4_OPTION_EOP;\r
+ Next++;\r
+ }\r
+\r
+ //\r
+ // Head length is in the unit of 4 bytes. Now, Len is the\r
+ // acutal option length to appear in the IP header.\r
+ //\r
+ Len = ((Next + 3) &~0x03);\r
+\r
+ //\r
+ // If the buffer is too small, set the BufLen then return\r
+ //\r
+ if ((Buf == NULL) || (*BufLen < Len)) {\r
+ *BufLen = Len;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // Copy the option to the Buf, zero the buffer first to pad\r
+ // the options with NOP to align to 4 bytes.\r
+ //\r
+ NetZeroMem (Buf, Len);\r
+ NetCopyMem (Buf, OptBuf, Next);\r
+ *BufLen = Len;\r
+ return EFI_SUCCESS;\r
+}\r