pxe_bc_ip.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 860 行 · 第 1/2 页

C
860
字号
                  Private,
                  GatewayIp,
                  sizeof (IPV4_HEADER),
                  sizeof (IPV4_HEADER),
                  MessagePtr,
                  IPV4_FRAG_SIZE,
                  Private->Function
                  );

      if (EFI_ERROR (StatCode)) {
        DEBUG (
          (EFI_D_WARN,
          "\nIp4Send()  Exit #3  %xh (%r)",
          StatCode,
          StatCode)
          );

        return StatCode;
      }

      MessagePtr += IPV4_FRAG_SIZE;
      MessageLength -= IPV4_FRAG_SIZE;
      FragmentOffset += IPV4_FRAG_OFF_INC;
      IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);
    }

    * (UINT8 *) (&IP_TX_HEADER.FragmentFields) &= ~(IP_MORE_FRAG >> 8);
    HdrSize = 0;
  }
  //
  // transmit
  //
  return Ipv4Xmt (
          Private,
          GatewayIp,
          sizeof (IPV4_HEADER),
          sizeof (IPV4_HEADER) + HdrSize,
          MessagePtr,
          MessageLength,
          Private->Function
          );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// return true if dst IP in receive header matched with what's enabled
//
STATIC
BOOLEAN
IPgood (
  PXE_BASECODE_DEVICE *Private,
  IPV4_HEADER         *IpHeader
  )
{
  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
  UINTN                   Index;

  PxeBcMode = Private->EfiBc.Mode;

  if (PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) {
    return TRUE;
  }

  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) &&
      IS_MULTICAST (&IpHeader->DestAddr)
        ) {
    return TRUE;
  }

  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) &&
      PxeBcMode->StationIp.Addr[0] == IpHeader->DestAddr.L
      ) {
    return TRUE;
  }

  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) && IpHeader->DestAddr.L == BROADCAST_IPv4) {
    return TRUE;
  }

  for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; ++Index) {
    if (IpHeader->DestAddr.L == PxeBcMode->IpFilter.IpList[Index].Addr[0]) {
      return TRUE;
    }
  }

  return FALSE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// receive up to MessageLength message into MessagePtr for protocol Prot
// return message length, src/dest ips if select any, and pointer to protocol
// header routine will filter based on source and/or dest ip if OpFlags set.
//
EFI_STATUS
IpReceive (
  PXE_BASECODE_DEVICE *Private,
  PXE_OPFLAGS         OpFlags,
  EFI_IP_ADDRESS      *SrcIpPtr,
  EFI_IP_ADDRESS      *DestIpPtr,
  UINT8               Prot,
  VOID                *HeaderPtr,
  UINTN               HdrSize,
  UINT8               *MessagePtr,
  UINTN               *MessageLengthPtr,
  EFI_EVENT           TimeoutEvent
  )
{
  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
  EFI_STATUS              StatCode;
  UINTN                   ByteCount;
  UINTN                   FragmentCount;
  UINTN                   ExpectedPacketLength;
  UINTN                   Id;
  BOOLEAN                 GotFirstFragment;
  BOOLEAN                 GotLastFragment;

  DEBUG (
    (EFI_D_NET,
    "\nIpReceive()  Hdr=%Xh  HdrSz=%d  Data=%Xh  DataSz=%d",
    HeaderPtr,
    HdrSize,
    MessagePtr,
    *MessageLengthPtr)
    );

  PxeBcMode                     = Private->EfiBc.Mode;
  PxeBcMode->IcmpErrorReceived  = FALSE;

  ExpectedPacketLength          = 0;
  GotFirstFragment              = FALSE;
  GotLastFragment               = FALSE;
  FragmentCount                 = 0;
  ByteCount                     = 0;
  Id = 0;

  for (;;) {
    IPV4_HEADER IpHdr;
    UINTN       FFlds;
    UINTN       TotalLength;
    UINTN       FragmentOffset;
    UINTN       HeaderSize;
    UINTN       BufferSize;
    UINTN       IpHeaderLength;
    UINTN       DataLength;
    UINT16      Protocol;
    UINT8       *NextHdrPtr;
    UINT8       *PacketPtr;

    StatCode = WaitForReceive (
                Private,
                Private->Function,
                TimeoutEvent,
                &HeaderSize,
                &BufferSize,
                &Protocol
                );

    if (EFI_ERROR (StatCode)) {
      return StatCode;
    }

    PacketPtr = Private->ReceiveBufferPtr + HeaderSize;

    if (Protocol == PXE_PROTOCOL_ETHERNET_ARP) {
      HandleArpReceive (
        Private,
        (ARP_PACKET *) PacketPtr,
        Private->ReceiveBufferPtr
        );

      continue;
    }

    if (Protocol != PXE_PROTOCOL_ETHERNET_IP) {
      continue;
    }

#if SUPPORT_IPV6
    if (PxeBcMode->UsingIpv6) {
      //
      // TBD
      //
    }
#endif

#define IpRxHeader  ((IPV4_HEADER *) PacketPtr)

    //
    // filter for version & check sum
    //
    IpHeaderLength = IPV4_HEADER_LENGTH (IpRxHeader);

    if ((IpRxHeader->VersionIhl >> 4) != IPVER4) {
      continue;
    }

    if (IpChecksum ((UINT16 *) IpRxHeader, IpHeaderLength)) {
      continue;
    }

    IpHdr       = *IpRxHeader;
    TotalLength = NTOHS (IpHdr.TotalLength);

    if (IpHdr.Protocol == PROT_TCP) {
      //
      // The NextHdrPtr is used to seed the header buffer we are passing back.
      // That being the case, we want to see everything in pPkt which contains
      // everything but the ethernet (or whatever) frame.  IP + TCP in this case.
      //
      DataLength  = TotalLength;
      NextHdrPtr  = PacketPtr;
    } else {
      DataLength  = TotalLength - IpHeaderLength;
      NextHdrPtr  = PacketPtr + IpHeaderLength;
    }
    //
    // If this is an ICMP, it might not be for us.
    // Double check the state of the IP stack and the
    // packet fields before assuming it is an ICMP
    // error.  ICMP requests are not supported by the
    // PxeBc IP stack and should be ignored.
    //
    if (IpHdr.Protocol == PROT_ICMP) {
      ICMPV4_HEADER *Icmpv4;

      Icmpv4 = (ICMPV4_HEADER *) NextHdrPtr;

      //
      // For now only obvious ICMP error replies will be accepted by
      // this stack.  This still makes us vulnerable to DoS attacks.
      // But at least we will not be killed by DHCP daemons.
      //
      switch (Icmpv4->Type) {
      case ICMP_REDIRECT:
      case ICMP_ECHO:
      case ICMP_ROUTER_ADV:
      case ICMP_ROUTER_SOLICIT:
      case ICMP_TIMESTAMP:
      case ICMP_TIMESTAMP_REPLY:
      case ICMP_INFO_REQ:
      case ICMP_INFO_REQ_REPLY:
      case ICMP_SUBNET_MASK_REQ:
      case ICMP_SUBNET_MASK_REPLY:
      default:
        continue;

      //
      // %%TBD - This should be implemented.
      //
      case ICMP_ECHO_REPLY:
        continue;

      case ICMP_DEST_UNREACHABLE:
      case ICMP_TIME_EXCEEDED:
      case ICMP_PARAMETER_PROBLEM:
      case ICMP_SOURCE_QUENCH:
        PxeBcMode->IcmpErrorReceived = TRUE;

        EfiCopyMem (
          &PxeBcMode->IcmpError,
          NextHdrPtr,
          sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
          );

        DEBUG (
          (EFI_D_NET,
          "\nIpReceive()  Exit #1  %Xh (%r)",
          EFI_ICMP_ERROR,
          EFI_ICMP_ERROR)
          );
      }

      return EFI_ICMP_ERROR;
    }

    if (IpHdr.Protocol == PROT_IGMP) {
      HandleIgmp (Private, (IGMPV2_MESSAGE *) NextHdrPtr, DataLength);

      DEBUG ((EFI_D_NET, "\n  IGMP"));
      continue;
    }
    //
    // check for protocol
    //
    if (IpHdr.Protocol != Prot) {
      continue;
    }
    //
    // do filtering
    //
    if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr && SrcIpPtr->Addr[0] != IpHdr.SrcAddr.L) {
      DEBUG ((EFI_D_NET, "\n  Not expected source IP address."));
      continue;
    }

    if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) {
      if (!IPgood (Private, &IpHdr)) {
        continue;
      }
    } else if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP)) {
      if (DestIpPtr == NULL) {
        if (PxeBcMode->StationIp.Addr[0] != IpHdr.DestAddr.L) {
          continue;
        }
      } else if (DestIpPtr->Addr[0] != IpHdr.DestAddr.L) {
        continue;
      }
    }
    //
    // get some data we need
    //
    FFlds           = NTOHS (IpHdr.FragmentFields);
    FragmentOffset  = ((FFlds & IP_FRAG_OFF_MSK) << 3);

    /* Keep count of fragments that belong to this session.
     * If we get packets with a different IP ID number,
     * ignore them.  Ignored packets should be handled
     * by the upper level protocol.
     */
    if (FragmentCount == 0) {
      Id = IpHdr.Id;

      if (DestIpPtr != NULL) {
        DestIpPtr->Addr[0] = IpHdr.DestAddr.L;
      }

      if (SrcIpPtr != NULL) {
        SrcIpPtr->Addr[0] = IpHdr.SrcAddr.L;
      }
    } else {
      if (IpHdr.Id != Id) {
        continue;
      }
    }

    ++FragmentCount;

    /* Fragment management.
     */
    if (FragmentOffset == 0) {
      /* This is the first fragment (may also be the
       * only fragment).
       */
      GotFirstFragment = TRUE;

      /* If there is a separate protocol header buffer,
       * copy the header, adjust the data pointer and
       * the data length.
       */
      if (HdrSize != 0) {
        EfiCopyMem (HeaderPtr, NextHdrPtr, HdrSize);

        NextHdrPtr += HdrSize;
        DataLength -= HdrSize;
      }
    } else {
      /* If there is a separate protocol header buffer,
       * adjust the fragment offset.
       */
      FragmentOffset -= HdrSize;
    }

    /* See if this is the last fragment.
     */
    if (!(FFlds & IP_MORE_FRAG)) {
      //
      // This is the last fragment (may also be the only fragment).
      //
      GotLastFragment = TRUE;

      /* Compute the expected length of the assembled
       * packet.  This will be used to decide if we
       * have gotten all of the fragments.
       */
      ExpectedPacketLength = FragmentOffset + DataLength;
    }

    DEBUG (
      (EFI_D_NET,
      "\n  ID = %Xh  Off = %d  Len = %d",
      Id,
      FragmentOffset,
      DataLength)
      );

    /* Check for receive buffer overflow.
     */
    if (FragmentOffset + DataLength > *MessageLengthPtr) {
      /* There is not enough space in the receive
       * buffer for the fragment.
       */
      DEBUG (
        (EFI_D_NET,
        "\nIpReceive()  Exit #3  %Xh (%r)",
        EFI_BUFFER_TOO_SMALL,
        EFI_BUFFER_TOO_SMALL)
        );

      return EFI_BUFFER_TOO_SMALL;
    }

    /* Copy data into receive buffer.
     */
    if (DataLength != 0) {
      DEBUG ((EFI_D_NET, "  To = %Xh", MessagePtr + FragmentOffset));

      EfiCopyMem (MessagePtr + FragmentOffset, NextHdrPtr, DataLength);
      ByteCount += DataLength;
    }

    /* If we have seen the first and last fragments and
     * the receive byte count is at least as large as the
     * expected byte count, return SUCCESS.
     *
     * We could be tricked by receiving a fragment twice
     * but the upper level protocol should figure this
     * out.
     */
    if (GotFirstFragment && GotLastFragment && ByteCount >= ExpectedPacketLength) {
      *MessageLengthPtr = ExpectedPacketLength;
      return EFI_SUCCESS;
    }
  }
}

/* eof - pxe_bc_ip.c */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?