/** @file IP4 option support functions. Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Ip4Impl.h" /** Validate the IP4 option format for both the packets we received and will transmit. @param[in] Option The first byte of the option @param[in] OptionLen The length of the whole option @param[in] Rcvd The option is from the packet we received if TRUE, otherwise the option we wants to transmit. @retval TRUE The option is properly formatted @retval FALSE The option is malformatted **/ BOOLEAN Ip4OptionIsValid ( IN UINT8 *Option, IN UINT32 OptionLen, IN BOOLEAN Rcvd ) { UINT32 Cur; UINT32 Len; UINT32 Point; Cur = 0; while (Cur < OptionLen) { switch (Option[Cur]) { case IP4_OPTION_NOP: Cur++; break; case IP4_OPTION_EOP: Cur = OptionLen; break; case IP4_OPTION_LSRR: case IP4_OPTION_SSRR: case IP4_OPTION_RR: Len = Option[Cur + 1]; Point = Option[Cur + 2]; // // SRR/RR options are formatted as |Type|Len|Point|Ip1|Ip2|... // if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) { return FALSE; } if ((Point > Len + 1) || (Point % 4 != 0)) { return FALSE; } // // The Point must point pass the last entry if the packet is received // by us. It must point to 4 if the packet is to be sent by us for // source route option. // if ((Option[Cur] != IP4_OPTION_RR) && ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) { return FALSE; } Cur += Len; break; default: Len = Option[Cur + 1]; if ((OptionLen - Cur < Len) || (Len < 2)) { return FALSE; } Cur = Cur + Len; break; } } return TRUE; } /** Copy the option from the original option to buffer. It handles the details such as: 1. whether copy the single IP4 option to the first/non-first fragments. 2. Pad the options copied over to aligned to 4 bytes. @param[in] Option The original option to copy from @param[in] OptionLen The length of the original option @param[in] FirstFragment Whether it is the first fragment @param[in, out] Buf The buffer to copy options to. NULL @param[in, out] BufLen The length of the buffer @retval EFI_SUCCESS The options are copied over @retval EFI_BUFFER_TOO_SMALL Buf is NULL or BufLen provided is too small. **/ EFI_STATUS Ip4CopyOption ( IN UINT8 *Option, IN UINT32 OptionLen, IN BOOLEAN FirstFragment, IN OUT UINT8 *Buf, OPTIONAL IN OUT UINT32 *BufLen ) { UINT8 OptBuf[40]; UINT32 Cur; UINT32 Next; UINT8 Type; UINT32 Len; ASSERT ((BufLen != NULL) && (OptionLen <= 40)); Cur = 0; Next = 0; while (Cur < OptionLen) { Type = Option[Cur]; Len = Option[Cur + 1]; if (Type == IP4_OPTION_NOP) { // // Keep the padding, in case that the sender wants to align // the option, say, to 4 bytes // OptBuf[Next] = IP4_OPTION_NOP; Next++; Cur++; } else if (Type == IP4_OPTION_EOP) { // // Don't append the EOP to avoid including only a EOP option // break; } else { // // don't copy options that is only valid for the first fragment // if (FirstFragment || (Type & IP4_OPTION_COPY_MASK) != 0) { CopyMem (OptBuf + Next, Option + Cur, Len); Next += Len; } Cur += Len; } } // // Don't append an EOP only option. // if (Next == 0) { *BufLen = 0; return EFI_SUCCESS; } // // Append an EOP if the end of option doesn't coincide with the // end of the IP header, that is, isn't aligned to 4 bytes.. // if ((Next % 4) != 0) { OptBuf[Next] = IP4_OPTION_EOP; Next++; } // // Head length is in the unit of 4 bytes. Now, Len is the // actual option length to appear in the IP header. // Len = ((Next + 3) &~0x03); // // If the buffer is too small, set the BufLen then return // if ((Buf == NULL) || (*BufLen < Len)) { *BufLen = Len; return EFI_BUFFER_TOO_SMALL; } // // Copy the option to the Buf, zero the buffer first to pad // the options with NOP to align to 4 bytes. // ZeroMem (Buf, Len); CopyMem (Buf, OptBuf, Next); *BufLen = Len; return EFI_SUCCESS; }