ip4input.c

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

C
1,332
字号
  //
  if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {

    NetListRemoveEntry (&Assemble->Link);

    //
    // If the packet is properly formated, the last fragment's End 
    // equals to the packet's total length. Otherwise, the packet 
    // is a fake, drop it now.
    //
    Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List);

    if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {
      Ip4FreeAssembleEntry (Assemble);
      return NULL;
    }

    //
    // Wrap the packet in a net buffer then deliver it up
    //
    NewPacket = NetbufFromBufList (
                  &Assemble->Fragments,
                  0,
                  0,
                  Ip4OnFreeFragments,
                  Assemble
                  );

    if (NewPacket == NULL) {
      Ip4FreeAssembleEntry (Assemble);
      return NULL;
    }

    NewPacket->Ip                  = Assemble->Head;
    *IP4_GET_CLIP_INFO (NewPacket) = *Assemble->Info;
    return NewPacket;
  }

  return NULL;

DROP:
  NetbufFree (Packet);
  return NULL;
}

VOID
Ip4AccpetFrame (
  IN IP4_PROTOCOL           *Ip4Instance,
  IN NET_BUF                *Packet,
  IN EFI_STATUS             IoStatus,
  IN UINT32                 Flag,
  IN VOID                   *Context
  )
/*++

Routine Description:

  The IP4 input routine. It is called by the IP4_INTERFACE when a 
  IP4 fragment is received from MNP. 

Arguments:

  Ip4Instance - The IP4 child that request the receive, most like it is NULL.
  Packet      - The IP4 packet received.
  IoStatus    - The return status of receive request.
  Flag        - The link layer flag for the packet received, such as multicast.
  Context     - The IP4 service instance that own the MNP.

Returns:

  None

--*/
{
  IP4_SERVICE               *IpSb;
  IP4_CLIP_INFO             *Info;
  IP4_HEAD                  *Head;
  UINT32                    HeadLen;
  UINT32                    OptionLen;
  UINT32                    TotalLen;
  UINT16                    Checksum;

  IpSb = (IP4_SERVICE *) Context;

  if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) {
    goto DROP;
  }
  
  //
  // Check that the IP4 header is correctly formated
  //
  if (Packet->TotalSize < IP4_MIN_HEADLEN) {
    goto RESTART;
  }

  Head     = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);
  HeadLen  = (Head->HeadLen << 2);
  TotalLen = NTOHS (Head->TotalLen);

  //
  // Mnp may deliver frame trailer sequence up, trim it off.
  //
  if (TotalLen < Packet->TotalSize) {
    NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);
  }

  if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) || 
      (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) {
    goto RESTART;
  }
  
  //
  // Some OS may send IP packets without checksum.
  //
  Checksum = ~NetblockChecksum ((UINT8 *) Head, HeadLen);

  if ((Head->Checksum != 0) && (Checksum != 0)) {
    goto RESTART;
  }

  //
  // Convert the IP header to host byte order, then get the per packet info.
  //
  Packet->Ip      = Ip4NtohHead (Head);

  Info            = IP4_GET_CLIP_INFO (Packet);
  Info->LinkFlag  = Flag;
  Info->CastType  = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);
  Info->Start     = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;
  Info->Length    = Head->TotalLen - HeadLen;
  Info->End       = Info->Start + Info->Length;
  Info->Status    = EFI_SUCCESS;

  //
  // The packet is destinated to us if the CastType is non-zero.
  //
  if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {
    goto RESTART;
  }

  //
  // Validate the options. Don't call the Ip4OptionIsValid if 
  // there is no option to save some CPU process.
  //
  OptionLen = HeadLen - IP4_MIN_HEADLEN;
  
  if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) {
    goto RESTART;
  }

  //
  // Trim the head off, after this point, the packet is headless.
  // and Packet->TotalLen == Info->Length.
  //
  NetbufTrim (Packet, HeadLen, TRUE);

  //
  // Reassemble the packet if this is a fragment. The packet is a
  // fragment if its head has MF (more fragment) set, or it starts
  // at non-zero byte.
  //
  if ((Head->Fragment & IP4_HEAD_MF_MASK) || (Info->Start != 0)) {
    //
    // Drop the fragment if DF is set but it is fragmented. Gateway 
    // need to send a type 4 destination unreache ICMP message here.
    //
    if (Head->Fragment & IP4_HEAD_DF_MASK) {
      goto RESTART;
    }
    
    //
    // The length of all but the last fragments is in the unit of 8 bytes.
    //
    if ((Head->Fragment & IP4_HEAD_MF_MASK) && (Info->Length % 8 != 0)) {
      goto RESTART;
    }

    Packet = Ip4Reassemble (&IpSb->Assemble, Packet);

    //
    // Packet assembly isn't complete, start receive more packet.
    //
    if (Packet == NULL) {
      goto RESTART;
    }
  }
  
  //
  // Packet may have been changed. Head, HeadLen, TotalLen, and
  // info must be reloaded bofore use. The ownership of the packet
  // is transfered to the packet process logic.
  //
  Head  = Packet->Ip;
  IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;

  switch (Head->Protocol) {
  case IP4_PROTO_ICMP:
    Ip4IcmpHandle (IpSb, Head, Packet);
    break;

  case IP4_PROTO_IGMP:
    Ip4IgmpHandle (IpSb, Head, Packet);
    break;

  default:
    Ip4Demultiplex (IpSb, Head, Packet);
  }

  Packet = NULL;

RESTART:
  Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);

DROP:
  if (Packet != NULL) {
    NetbufFree (Packet);
  }

  return ;
}

BOOLEAN
Ip4InstanceFrameAcceptable (
  IN IP4_PROTOCOL           *IpInstance,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
/*++

Routine Description:

  Check whether this IP child accepts the packet.

Arguments:

  IpInstance  - The IP child to check
  Head        - The IP header of the packet
  Packet      - The data of the packet

Returns:

  TRUE if the child wants to receive the packet, otherwise return FALSE.

--*/
{
  IP4_ICMP_ERROR_HEAD       Icmp;
  EFI_IP4_CONFIG_DATA       *Config;
  IP4_CLIP_INFO             *Info;
  UINT16                    Proto;
  UINT32                    Index;

  Config = &IpInstance->ConfigData;

  //
  // Dirty trick for the Tiano UEFI network stack implmentation. If 
  // ReceiveTimeout == -1, the receive of the packet for this instance
  // is disabled. The UEFI spec don't have such captibility. We add 
  // this to improve the performance because IP will make a copy of
  // the received packet for each accepting instance. Some IP instances
  // used by UDP/TCP only send packets, they don't wants to receive.
  //
  if (Config->ReceiveTimeout == (UINT32)(-1)) {
    return FALSE;
  }
  
  if (Config->AcceptPromiscuous) {
    return TRUE;
  }
  
  //
  // Use protocol from the IP header embedded in the ICMP error
  // message to filter, instead of ICMP itself. ICMP handle will
  // can Ip4Demultiplex to deliver ICMP errors.
  //
  Proto = Head->Protocol;

  if (Proto == IP4_PROTO_ICMP) {
    NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head);

    if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
      if (!Config->AcceptIcmpErrors) {
        return FALSE;
      }

      NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
      Proto = Icmp.IpHead.Protocol;
    }
  }

  //
  // Match the protocol
  //
  if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) {
    return FALSE;
  }

  //
  // Check for broadcast, the caller has computed the packet's 
  // cast type for this child's interface.
  //
  Info = IP4_GET_CLIP_INFO (Packet);

  if (IP4_IS_BROADCAST (Info->CastType)) {
    return Config->AcceptBroadcast; 
  } 

  //
  // If it is a multicast packet, check whether we are in the group.
  //
  if (Info->CastType == IP4_MULTICAST) {
    //
    // Receive the multicast if the instance wants to receive all packets.
    //
    if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {
      return TRUE;
    }
    
    for (Index = 0; Index < IpInstance->GroupCount; Index++) {
      if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {
        break;
      }
    }

    return (BOOLEAN)(Index < IpInstance->GroupCount);
  }

  return TRUE;
}

EFI_STATUS
Ip4InstanceEnquePacket (
  IN IP4_PROTOCOL           *IpInstance,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
/*++

Routine Description:

  Enqueue a shared copy of the packet to the IP4 child if the 
  packet is acceptable to it. Here the data of the packet is 
  shared, but the net buffer isn't.

Arguments:

  IpInstance  - The IP4 child to enqueue the packet to
  Head        - The IP header of the received packet
  Packet      - The data of the received packet

Returns:

  EFI_NOT_STARTED       - The IP child hasn't been configured.
  EFI_INVALID_PARAMETER - The child doesn't want to receive the packet
  EFI_OUT_OF_RESOURCES  - Failed to allocate some resource
  EFI_SUCCESS           - A shared copy the packet is enqueued to the child.

--*/
{
  IP4_CLIP_INFO             *Info;
  NET_BUF                   *Clone;

  //
  // Check whether the packet is acceptable to this instance.
  //
  if (IpInstance->State != IP4_STATE_CONFIGED) {
    return EFI_NOT_STARTED;
  }

  if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Enque a shared copy of the packet.
  //
  Clone = NetbufClone (Packet);

  if (Clone == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Set the receive time out for the assembled packet. If it expires,
  // packet will be removed from the queue.
  //
  Info        = IP4_GET_CLIP_INFO (Clone);
  Info->Life  = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);

  NetListInsertTail (&IpInstance->Received, &Clone->List);
  return EFI_SUCCESS;
}

STATIC
VOID
EFIAPI
Ip4OnRecyclePacket (
  IN EFI_EVENT              Event,
  IN VOID                   *Context
  )
/*++

Routine Description:

  The signal handle of IP4's recycle event. It is called back
  when the upper layer release the packet.

Arguments:

  Event   - The IP4's recycle event.
  Context - The context of the handle, which is a IP4_RXDATA_WRAP

Returns:

  None

--*/
{
  IP4_RXDATA_WRAP           *Wrap;

  Wrap = (IP4_RXDATA_WRAP *) Context;

  NET_TRYLOCK (&Wrap->IpInstance->RecycleLock);
  NetListRemoveEntry (&Wrap->Link);
  NET_UNLOCK (&Wrap->IpInstance->RecycleLock);

  ASSERT (!NET_BUF_SHARED (Wrap->Packet));
  NetbufFree (Wrap->Packet);

  gBS->CloseEvent (Wrap->RxData.RecycleSignal);
  NetFreePool (Wrap);
}

IP4_RXDATA_WRAP *
Ip4WrapRxData (
  IN IP4_PROTOCOL           *IpInstance,
  IN NET_BUF                *Packet
  )
/*++

Routine Description:

  Wrap the received packet to a IP4_RXDATA_WRAP, which will be 
  delivered to the upper layer. Each IP4 child that accepts the
  packet will get a not-shared copy of the packet which is wrapped

⌨️ 快捷键说明

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