git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@219 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			862 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			862 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*++
 | 
						|
 | 
						|
Copyright (c) 2006, Intel Corporation                                                         
 | 
						|
All rights reserved. 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.             
 | 
						|
 | 
						|
Module Name:
 | 
						|
  pxe_bc_ip.c
 | 
						|
 | 
						|
Abstract:
 | 
						|
 | 
						|
--*/
 | 
						|
 | 
						|
 | 
						|
#include "Bc.h"
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
BOOLEAN
 | 
						|
OnSameSubnet (
 | 
						|
  IN UINTN           IpLength,
 | 
						|
  IN EFI_IP_ADDRESS  *Ip1,
 | 
						|
  IN EFI_IP_ADDRESS  *Ip2,
 | 
						|
  IN EFI_IP_ADDRESS  *SubnetMask
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Check if two IP addresses are on the same subnet.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    IpLength   - Length of IP address in bytes.
 | 
						|
    Ip1        - IP address to check.
 | 
						|
    Ip2        - IP address to check.
 | 
						|
    SubnetMask - Subnet mask to check with.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    TRUE       - IP addresses are on the same subnet.
 | 
						|
    FALSE      - IP addresses are on different subnets.
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  if (IpLength == 0 || Ip1 == NULL || Ip2 == NULL || SubnetMask == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  while (IpLength-- != 0) {
 | 
						|
    if ((Ip1->v6.Addr[IpLength] ^ Ip2->v6.Addr[IpLength]) & SubnetMask->v6.Addr[IpLength]) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
VOID
 | 
						|
IpAddRouter (
 | 
						|
  IN PXE_BASECODE_DEVICE *Private,
 | 
						|
  IN EFI_IP_ADDRESS      *RouterIpPtr
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Add router to router table.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    Private     - Pointer PxeBc instance data.
 | 
						|
    RouterIpPtr - Pointer to router IP address.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    Nothing
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
 | 
						|
  UINTN                   Index;
 | 
						|
 | 
						|
  if (Private == NULL || RouterIpPtr == NULL) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  PxeBcMode = Private->EfiBc.Mode;
 | 
						|
 | 
						|
  //
 | 
						|
  // if we are filled up or this is not on the same subnet, forget it
 | 
						|
  //
 | 
						|
  if ((PxeBcMode->RouteTableEntries == PXE_ROUTER_TABLE_SIZE) ||
 | 
						|
    !OnSameSubnet(Private->IpLength, &PxeBcMode->StationIp, RouterIpPtr, &PxeBcMode->SubnetMask)) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // make sure we don't already have it
 | 
						|
  //
 | 
						|
  for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
 | 
						|
    if (!CompareMem (
 | 
						|
          &PxeBcMode->RouteTable[Index].GwAddr,
 | 
						|
          RouterIpPtr,
 | 
						|
          Private->IpLength
 | 
						|
          )) {
 | 
						|
      return ;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // keep it
 | 
						|
  //
 | 
						|
  ZeroMem (
 | 
						|
    &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries],
 | 
						|
    sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY)
 | 
						|
    );
 | 
						|
 | 
						|
  CopyMem (
 | 
						|
    &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries++].GwAddr,
 | 
						|
    RouterIpPtr,
 | 
						|
    Private->IpLength
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
//
 | 
						|
// return router ip to use for DestIp (0 if none)
 | 
						|
//
 | 
						|
STATIC
 | 
						|
EFI_IP_ADDRESS *
 | 
						|
GetRouterIp (
 | 
						|
  PXE_BASECODE_DEVICE *Private,
 | 
						|
  EFI_IP_ADDRESS      *DestIpPtr
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
 | 
						|
  UINTN                   Index;
 | 
						|
 | 
						|
  if (Private == NULL || DestIpPtr == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  PxeBcMode = Private->EfiBc.Mode;
 | 
						|
 | 
						|
  for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
 | 
						|
    if (OnSameSubnet (
 | 
						|
          Private->IpLength,
 | 
						|
          &PxeBcMode->RouteTable[Index].IpAddr,
 | 
						|
          DestIpPtr,
 | 
						|
          &PxeBcMode->RouteTable[Index].SubnetMask
 | 
						|
          )) {
 | 
						|
      return &PxeBcMode->RouteTable[Index].GwAddr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
//
 | 
						|
// routine to send ipv4 packet
 | 
						|
// ipv4 header of length HdrLth in TransmitBufferPtr
 | 
						|
// routine fills in ipv4hdr Ver_Hdl, TotalLength, and Checksum, moves in Data
 | 
						|
// and gets dest MAC address
 | 
						|
//
 | 
						|
#define IP_TX_BUFFER  ((IPV4_BUFFER *) Private->TransmitBufferPtr)
 | 
						|
#define IP_TX_HEADER  IP_TX_BUFFER->IpHeader
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
Ipv4Xmt (
 | 
						|
  PXE_BASECODE_DEVICE         *Private,
 | 
						|
  UINT32                      GatewayIp,
 | 
						|
  UINTN                       IpHeaderLength,
 | 
						|
  UINTN                       TotalHeaderLength,
 | 
						|
  VOID                        *Data,
 | 
						|
  UINTN                       DataLength,
 | 
						|
  EFI_PXE_BASE_CODE_FUNCTION  Function
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_MAC_ADDRESS             DestMac;
 | 
						|
  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
 | 
						|
  EFI_PXE_BASE_CODE_MODE      *PxeBcMode;
 | 
						|
  EFI_STATUS                  StatCode;
 | 
						|
  UINTN                       PacketLength;
 | 
						|
 | 
						|
  Snp           = Private->SimpleNetwork;
 | 
						|
  PxeBcMode     = Private->EfiBc.Mode;
 | 
						|
  StatCode      = EFI_SUCCESS;
 | 
						|
  PacketLength  = TotalHeaderLength + DataLength;
 | 
						|
 | 
						|
  //
 | 
						|
  // get dest MAC address
 | 
						|
  // multicast - convert to hw equiv
 | 
						|
  // unicast on same net, use arp
 | 
						|
  // on different net, arp for router
 | 
						|
  //
 | 
						|
  if (IP_TX_HEADER.DestAddr.L == BROADCAST_IPv4) {
 | 
						|
    CopyMem (&DestMac, &Snp->Mode->BroadcastAddress, sizeof (DestMac));
 | 
						|
  } else if (IS_MULTICAST (&IP_TX_HEADER.DestAddr)) {
 | 
						|
    StatCode = (*Snp->MCastIpToMac) (Snp, PxeBcMode->UsingIpv6, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, &DestMac);
 | 
						|
  } else {
 | 
						|
    UINT32  Ip;
 | 
						|
 | 
						|
    if (OnSameSubnet (
 | 
						|
          Private->IpLength,
 | 
						|
          &PxeBcMode->StationIp,
 | 
						|
          (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr,
 | 
						|
          &PxeBcMode->SubnetMask
 | 
						|
          )) {
 | 
						|
      Ip = IP_TX_HEADER.DestAddr.L;
 | 
						|
    } else if (GatewayIp != 0) {
 | 
						|
      Ip = GatewayIp;
 | 
						|
    } else {
 | 
						|
      EFI_IP_ADDRESS  *TmpIp;
 | 
						|
 | 
						|
      TmpIp = GetRouterIp (Private, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr);
 | 
						|
 | 
						|
      if (TmpIp == NULL) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_WARN,
 | 
						|
          "\nIpv4Xmit()  Exit #1  %xh (%r)",
 | 
						|
          EFI_NO_RESPONSE,
 | 
						|
          EFI_NO_RESPONSE)
 | 
						|
          );
 | 
						|
 | 
						|
        return EFI_NO_RESPONSE;
 | 
						|
        //
 | 
						|
        // no router
 | 
						|
        //
 | 
						|
      }
 | 
						|
 | 
						|
      Ip = TmpIp->Addr[0];
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GetHwAddr (
 | 
						|
          Private,
 | 
						|
          (EFI_IP_ADDRESS *) &Ip,
 | 
						|
          (EFI_MAC_ADDRESS *) &DestMac
 | 
						|
          )) {
 | 
						|
      if (!PxeBcMode->AutoArp) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_WARN,
 | 
						|
          "\nIpv4Xmit()  Exit #2  %xh (%r)",
 | 
						|
          EFI_DEVICE_ERROR,
 | 
						|
          EFI_DEVICE_ERROR)
 | 
						|
          );
 | 
						|
 | 
						|
        return EFI_DEVICE_ERROR;
 | 
						|
      } else {
 | 
						|
        StatCode = DoArp (
 | 
						|
                    Private,
 | 
						|
                    (EFI_IP_ADDRESS *) &Ip,
 | 
						|
                    (EFI_MAC_ADDRESS *) &DestMac
 | 
						|
                    );
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (StatCode)) {
 | 
						|
    DEBUG ((EFI_D_WARN, "\nIpv4Xmit()  Exit #3  %xh (%r)", StatCode, StatCode));
 | 
						|
    return StatCode;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // fill in packet info
 | 
						|
  //
 | 
						|
  SET_IPV4_VER_HDL (&IP_TX_HEADER, IpHeaderLength);
 | 
						|
  IP_TX_HEADER.TotalLength    = HTONS (PacketLength);
 | 
						|
  IP_TX_HEADER.HeaderChecksum = IpChecksum ((UINT16 *) &IP_TX_HEADER, IpHeaderLength);
 | 
						|
  CopyMem (((UINT8 *) &IP_TX_HEADER) + TotalHeaderLength, Data, DataLength);
 | 
						|
 | 
						|
  //
 | 
						|
  // send it
 | 
						|
  //
 | 
						|
  return SendPacket (
 | 
						|
          Private,
 | 
						|
          (UINT8 *) &IP_TX_HEADER - Snp->Mode->MediaHeaderSize,
 | 
						|
          &IP_TX_HEADER,
 | 
						|
          PacketLength,
 | 
						|
          &DestMac,
 | 
						|
          PXE_PROTOCOL_ETHERNET_IP,
 | 
						|
          Function
 | 
						|
          );
 | 
						|
}
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
//
 | 
						|
// send ipv4 packet with option
 | 
						|
//
 | 
						|
EFI_STATUS
 | 
						|
Ipv4SendWOp (
 | 
						|
  PXE_BASECODE_DEVICE         *Private,
 | 
						|
  UINT32                      GatewayIp,
 | 
						|
  UINT8                       *Msg,
 | 
						|
  UINTN                       MessageLength,
 | 
						|
  UINT8                       Prot,
 | 
						|
  UINT8                       *Option,
 | 
						|
  UINTN                       OptionLength,
 | 
						|
  UINT32                      DestIp,
 | 
						|
  EFI_PXE_BASE_CODE_FUNCTION  Function
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
 | 
						|
  UINTN                   HdrLth;
 | 
						|
 | 
						|
  PxeBcMode = Private->EfiBc.Mode;
 | 
						|
  HdrLth    = sizeof (IPV4_HEADER) + OptionLength;
 | 
						|
 | 
						|
  ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));
 | 
						|
  IP_TX_HEADER.TimeToLive     = PxeBcMode->TTL;
 | 
						|
  IP_TX_HEADER.TypeOfService  = PxeBcMode->ToS;
 | 
						|
  IP_TX_HEADER.Protocol       = Prot;
 | 
						|
  IP_TX_HEADER.SrcAddr.L      = *(UINT32 *) &PxeBcMode->StationIp;
 | 
						|
  IP_TX_HEADER.DestAddr.L     = DestIp;
 | 
						|
  IP_TX_HEADER.Id             = Random (Private);
 | 
						|
  CopyMem (IP_TX_BUFFER->u.Data, Option, OptionLength);
 | 
						|
  return Ipv4Xmt (
 | 
						|
          Private,
 | 
						|
          GatewayIp,
 | 
						|
          HdrLth,
 | 
						|
          HdrLth,
 | 
						|
          Msg,
 | 
						|
          MessageLength,
 | 
						|
          Function
 | 
						|
          );
 | 
						|
}
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
//
 | 
						|
// send MessageLength message at MessagePtr - higher level protocol header already in TransmitBufferPtr, length HdrSize
 | 
						|
//
 | 
						|
EFI_STATUS
 | 
						|
Ip4Send (
 | 
						|
  PXE_BASECODE_DEVICE                          *Private,  // pointer to instance data
 | 
						|
  UINTN               MayFrag,                            //
 | 
						|
  UINT8                                    Prot,          // protocol
 | 
						|
  UINT32                          SrcIp,                  // Source IP address
 | 
						|
  UINT32                 DestIp,                          // Destination IP address
 | 
						|
  UINT32              GatewayIp,                          // used if not NULL and needed
 | 
						|
  UINTN               HdrSize,                            // protocol header byte length
 | 
						|
  UINT8               *MessagePtr,                        // pointer to data
 | 
						|
  UINTN               MessageLength                       // data byte length
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  StatCode;
 | 
						|
  UINTN       TotDataLength;
 | 
						|
 | 
						|
  TotDataLength = HdrSize + MessageLength;
 | 
						|
 | 
						|
  if (TotDataLength > MAX_IPV4_DATA_SIZE) {
 | 
						|
    DEBUG (
 | 
						|
      (EFI_D_WARN,
 | 
						|
      "\nIp4Send()  Exit #1  %xh (%r)",
 | 
						|
      EFI_BAD_BUFFER_SIZE,
 | 
						|
      EFI_BAD_BUFFER_SIZE)
 | 
						|
      );
 | 
						|
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));
 | 
						|
  IP_TX_HEADER.TimeToLive = DEFAULT_TTL;
 | 
						|
  IP_TX_HEADER.Protocol   = Prot;
 | 
						|
  IP_TX_HEADER.SrcAddr.L  = SrcIp;
 | 
						|
  IP_TX_HEADER.DestAddr.L = DestIp;
 | 
						|
  IP_TX_HEADER.Id         = Random (Private);
 | 
						|
 | 
						|
  if (!MayFrag) {
 | 
						|
    *(UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_NO_FRAG >> 8;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // check for need to fragment
 | 
						|
  //
 | 
						|
  if (TotDataLength > MAX_IPV4_FRAME_DATA_SIZE) {
 | 
						|
    UINTN   DataLengthSent;
 | 
						|
    UINT16  FragmentOffset;
 | 
						|
 | 
						|
    FragmentOffset = IP_MORE_FRAG;
 | 
						|
    //
 | 
						|
    // frag offset field
 | 
						|
    //
 | 
						|
    if (!MayFrag) {
 | 
						|
      DEBUG (
 | 
						|
        (EFI_D_WARN,
 | 
						|
        "\nIp4Send()  Exit #2  %xh (%r)",
 | 
						|
        EFI_BAD_BUFFER_SIZE,
 | 
						|
        EFI_BAD_BUFFER_SIZE)
 | 
						|
        );
 | 
						|
 | 
						|
      return EFI_BAD_BUFFER_SIZE;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // send out in fragments - first includes upper level header
 | 
						|
    // all are max and include more frag bit except last
 | 
						|
    //
 | 
						|
    * (UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_MORE_FRAG >> 8;
 | 
						|
 | 
						|
#define IPV4_FRAG_SIZE    (MAX_IPV4_FRAME_DATA_SIZE & 0xfff8)
 | 
						|
#define IPV4_FRAG_OFF_INC (IPV4_FRAG_SIZE >> 3)
 | 
						|
 | 
						|
    DataLengthSent = IPV4_FRAG_SIZE - HdrSize;
 | 
						|
 | 
						|
    StatCode = Ipv4Xmt (
 | 
						|
                Private,
 | 
						|
                GatewayIp,
 | 
						|
                sizeof (IPV4_HEADER),
 | 
						|
                sizeof (IPV4_HEADER) + HdrSize,
 | 
						|
                MessagePtr,
 | 
						|
                DataLengthSent,
 | 
						|
                Private->Function
 | 
						|
                );
 | 
						|
 | 
						|
    if (EFI_ERROR (StatCode)) {
 | 
						|
      DEBUG (
 | 
						|
        (EFI_D_WARN,
 | 
						|
        "\nIp4Send()  Exit #3  %xh (%r)",
 | 
						|
        StatCode,
 | 
						|
        StatCode)
 | 
						|
        );
 | 
						|
 | 
						|
      return StatCode;
 | 
						|
    }
 | 
						|
 | 
						|
    MessagePtr += DataLengthSent;
 | 
						|
    MessageLength -= DataLengthSent;
 | 
						|
    FragmentOffset += IPV4_FRAG_OFF_INC;
 | 
						|
    IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);
 | 
						|
 | 
						|
    while (MessageLength > IPV4_FRAG_SIZE) {
 | 
						|
      StatCode = Ipv4Xmt (
 | 
						|
                  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;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (&IpHdr, IpRxHeader, sizeof (IpHdr));
 | 
						|
    //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;
 | 
						|
 | 
						|
        CopyMem (
 | 
						|
          &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) {
 | 
						|
        CopyMem (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));
 | 
						|
 | 
						|
      CopyMem (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 */
 |