git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			847 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			847 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
| Copyright (c) 2004, 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"
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| /**
 | |
|   Check if two IP addresses are on the same subnet.
 | |
| 
 | |
|   @param  IpLength     Length of IP address in bytes.
 | |
|   @param  Ip1          IP address to check.
 | |
|   @param  Ip2          IP address to check.
 | |
|   @param  SubnetMask   Subnet mask to check with.
 | |
| 
 | |
|   @retval TRUE         IP addresses are on the same subnet.
 | |
|   @retval FALSE        IP addresses are on different subnets.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| OnSameSubnet (
 | |
|   IN UINTN           IpLength,
 | |
|   IN EFI_IP_ADDRESS  *Ip1,
 | |
|   IN EFI_IP_ADDRESS  *Ip2,
 | |
|   IN EFI_IP_ADDRESS  *SubnetMask
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| /**
 | |
|   Add router to router table.
 | |
| 
 | |
|   @param  Private      Pointer PxeBc instance data.
 | |
|   @param  RouterIpPtr  Pointer to router IP address.
 | |
| 
 | |
|   @return Nothing
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| IpAddRouter (
 | |
|   IN PXE_BASECODE_DEVICE *Private,
 | |
|   IN EFI_IP_ADDRESS      *RouterIpPtr
 | |
|   )
 | |
| {
 | |
|   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 (
 | |
|           (DEBUG_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 (
 | |
|           (DEBUG_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 ((DEBUG_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 (
 | |
|       (DEBUG_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 (
 | |
|         (DEBUG_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 (
 | |
|         (DEBUG_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 (
 | |
|           (DEBUG_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 (
 | |
|     (DEBUG_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;
 | |
|     }
 | |
| 
 | |
| #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));
 | |
|     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 (
 | |
|           (DEBUG_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 ((DEBUG_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 ((DEBUG_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 (
 | |
|       (DEBUG_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 (
 | |
|         (DEBUG_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 ((DEBUG_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 */
 |