【UEFI基础】EDK网络框架(UDP4)

UDP4

UDP4协议说明

UDP的全称是User Datagram Protocol,它不提供复杂的控制机制,仅利用IP提供面向无连接的通信服务。它将上层应用程序发来的数据在收到的那一刻,立即按照原样发送到网络。

UDP报文格式:

在这里插入图片描述

各个参数说明如下:

字段长度(字节)描述
Source Port2发送端口,标识哪个应用程序发送(发送进程)。
Destination Port2目标端口,标识哪个应用程序接收(接收进程)。
Length2UDP首部加上UDP数据的字节数,最小为8。
Checksum2覆盖UDP首部和UDP数据,是可选的。
data octets变长UDP负载,可选的。

前面的四个参数对应到UEFI代码中就是UDP头部:

//
// UDP header definition
//
typedef struct {
  UINT16    SrcPort;
  UINT16    DstPort;
  UINT16    Length;
  UINT16    Checksum;
} EFI_UDP_HEADER;

UDP4代码综述

UDP4也是一个通用的网络协议,其实现在NetworkPkg\Udp4Dxe\Udp4Dxe.inf,这里首先需要看下它的入口:

EFI_STATUS
EFIAPI
Udp4DriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Install the Udp4DriverBinding and Udp4ComponentName protocols.
  //
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &gUdp4DriverBinding,
             ImageHandle,
             &gUdp4ComponentName,
             &gUdp4ComponentName2
             );
  if (!EFI_ERROR (Status)) {
    //
    // Initialize the UDP random port.
    //
    mUdp4RandomPort = (UINT16)(((UINT16)NetRandomInitSeed ()) % UDP4_PORT_KNOWN + UDP4_PORT_KNOWN); // 宏的值是1024
  }
}

因为UDP4也是一个UEFI Driver Model,所以第一步是安装gUdp4DriverBinding,其实现:

EFI_DRIVER_BINDING_PROTOCOL  gUdp4DriverBinding = {
  Udp4DriverBindingSupported,
  Udp4DriverBindingStart,
  Udp4DriverBindingStop,
  0xa,
  NULL,
  NULL
};

而第二步是初始化一个随机的UDP端口,根据通用网络协议的做法,UDP的端口占两个字节(即16位),只要不是0-1023里面的公认端口都可以,且跟TCP端口的一致也没有关系。

UDP4在UEFI网络协议栈中的关系图:

支持
提供
支持
支持
提供
支持
提供
提供
提供
支持
提供
提供
支持
支持
提供
提供
提供
支持
提供
提供
gEfiPciIoProtocolGuid
UNDI
gEfiNetworkInterfaceIdentifierProtocolGuid_31
gEfiDevicePathProtocolGuid
SNP
gEfiSimpleNetworkProtocolGuid
MNP
gEfiVlanConfigProtocolGuid
gEfiManagedNetworkServiceBindingProtocolGuid
gEfiManagedNetworkProtocolGuid
ARP
gEfiArpServiceBindingProtocolGuid
gEfiArpProtocolGuid
IP4
gEfiIp4ServiceBindingProtocolGuid
gEfiIp4Config2ProtocolGuid
gEfiIp4ProtocolGuid
UDP4
gEfiUdp4ServiceBindingProtocolGuid
gEfiUdp4ProtocolGuid

Udp4DriverBindingSupported

UDP4依赖于IP4:

EFI_STATUS
EFIAPI
Udp4DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  )
{
  //
  // Test for the Ip4 Protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiIp4ServiceBindingProtocolGuid,
                  NULL,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                  );
}

Udp4DriverBindingStart

Start函数的流程大致如下:

  1. 初始化UDP4_SERVICE_DATA
  2. 安装gEfiUdp4ServiceBindingProtocolGuid

同其它驱动一样,重点也是结构体,这里就是UDP4_SERVICE_DATA

UDP4_SERVICE_DATA

UDP4_SERVICE_DATA在Start函数中创建:

EFI_STATUS
EFIAPI
Udp4DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  )
{
  Status = Udp4CreateService (Udp4Service, This->DriverBindingHandle, ControllerHandle);
}

其结构体定义如下:

typedef struct _UDP4_SERVICE_DATA_ {
  UINT32                          Signature;
  EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;
  EFI_HANDLE                      ImageHandle;
  EFI_HANDLE                      ControllerHandle;
  LIST_ENTRY                      ChildrenList;
  UINTN                           ChildrenNumber;
  IP_IO                           *IpIo;

  EFI_EVENT                       TimeoutEvent;
} UDP4_SERVICE_DATA;

相比之前的服务数据,这个结构体相当得简单,其中比较重要的成员有:

  • ServiceBinding:对应mUdp4ServiceBinding
EFI_SERVICE_BINDING_PROTOCOL  mUdp4ServiceBinding = {
  Udp4ServiceBindingCreateChild,
  Udp4ServiceBindingDestroyChild
};

用于创建UDP4子项。

  • ChildrenListChildrenNumber:对应UDP4_INSTANCE_DATA结构体,由Udp4ServiceBindingCreateChild()创建,是表示子项的结构体,在UDP4_INSTANCE_DATA会进一步介绍。
  • IpIo:它是对IP4实例的一个包装,UDP4通过它来进行通信,在IP_IO已经介绍过。在UDP4以及TCP4中会进场看到类似IP_IO这样的结构体,它们仅仅是对IP层的保证,之所以要有这样的包装,是因为存在IPv4和IPv6两个版本,而这里只关注IPv4。
  • TimeoutEvent:一个定时事件,创建的位置是在Udp4CreateService()函数中:
EFI_STATUS
Udp4CreateService (
  IN OUT UDP4_SERVICE_DATA  *Udp4Service,
  IN     EFI_HANDLE         ImageHandle,
  IN     EFI_HANDLE         ControllerHandle
  )
{
  //
  // Create the event for Udp timeout checking.
  //
  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  Udp4CheckTimeout,
                  Udp4Service,
                  &Udp4Service->TimeoutEvent
                  );
  //
  // Start the timeout timer event.
  //
  Status = gBS->SetTimer (
                  Udp4Service->TimeoutEvent,
                  TimerPeriodic,
                  UDP4_TIMEOUT_INTERVAL // 50 milliseconds
                  );

对应的回调函数Udp4CheckTimeout(),它用来检测接收到的报文是否过期,其主体代码:

VOID
EFIAPI
Udp4CheckTimeout (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
    //
    // Iterate all the instances belonging to this service context.
    //
    Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
    NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE);

    if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {
      //
      // Skip this instance if it's not configured or no receive timeout.
      //
      continue;
    }

    NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {
      //
      // Iterate all the rxdatas belonging to this udp instance.
      //
      Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP4_RXDATA_WRAP, Link);

      //
      // TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns.
      //
      if (Wrap->TimeoutTick < (UDP4_TIMEOUT_INTERVAL / 10)) {
        //
        // Remove this RxData if it timeouts.
        //
        Udp4RecycleRxDataWrap (NULL, (VOID *)Wrap);
      } else {
        Wrap->TimeoutTick -= (UDP4_TIMEOUT_INTERVAL / 10);
      }
    }
  }
}

这里的Wrap对应结构体UDP4_RXDATA_WRAP

typedef struct _UDP4_RXDATA_WRAP_ {
  LIST_ENTRY               Link;
  NET_BUF                  *Packet;
  UINT32                   TimeoutTick;
  EFI_UDP4_RECEIVE_DATA    RxData;
} UDP4_RXDATA_WRAP;

它通过Udp4WrapRxData()创建,然后放到一个队列中供UDP驱动处理,如果来不及处理就会过期,而过期时间也由这里的成员TimeoutTick指定,而该成员由另外的一个值指定:

Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;

Instance是后面会介绍的UDP4_INSTANCE_DATA中的配置参数ConfigDataConfigData.ReceiveTimeout的值在创建时是-1,表示不会过期:

  //
  // use the -1 magic number to disable the receiving process of the ip instance.
  //
  Ip4ConfigData->ReceiveTimeout = (UINT32)(-1);

不过在UDP4的配置中可以修改:

EFI_STATUS
EFIAPI
Udp4Configure (
  IN EFI_UDP4_PROTOCOL     *This,
  IN EFI_UDP4_CONFIG_DATA  *UdpConfigData OPTIONAL
  )
{
  if (UdpConfigData != NULL) {
    if (Instance->Configured) {
      //
      // Save the reconfigurable parameters.
      //
      Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;
    }
  }
}

UDP4_INSTANCE_DATA

UDP4_INSTANCE_DATA表示一个UDP4子项,其它位于NetworkPkg\Udp4Dxe\Udp4Impl.h:

typedef struct _UDP4_INSTANCE_DATA_ {
  UINT32                  Signature;
  LIST_ENTRY              Link;

  UDP4_SERVICE_DATA       *Udp4Service;
  EFI_UDP4_PROTOCOL       Udp4Proto;
  EFI_UDP4_CONFIG_DATA    ConfigData;
  EFI_HANDLE              ChildHandle;
  BOOLEAN                 Configured;
  BOOLEAN                 IsNoMapping;

  NET_MAP                 TxTokens;
  NET_MAP                 RxTokens;

  NET_MAP                 McastIps;

  LIST_ENTRY              RcvdDgramQue;
  LIST_ENTRY              DeliveredDgramQue;

  UINT16                  HeadSum;

  EFI_STATUS              IcmpError;

  IP_IO_IP_INFO           *IpInfo;

  BOOLEAN                 InDestroy;
} UDP4_INSTANCE_DATA;

下面介绍其中比较重要的成员:

  • Udp4Service:指向UDP4服务的结构体。

  • Udp4Proto:对应EFI_UDP4_PROTOCOL,后面会进一步介绍。

  • ConfigData:UDP配置数据:

typedef struct {
  //
  // Receiving Filters
  //
  BOOLEAN             AcceptBroadcast;
  BOOLEAN             AcceptPromiscuous;
  BOOLEAN             AcceptAnyPort;
  BOOLEAN             AllowDuplicatePort;
  //
  // I/O parameters
  //
  UINT8               TypeOfService;
  UINT8               TimeToLive;
  BOOLEAN             DoNotFragment;
  UINT32              ReceiveTimeout;
  UINT32              TransmitTimeout;
  //
  // Access Point
  //
  BOOLEAN             UseDefaultAddress;
  EFI_IPv4_ADDRESS    StationAddress;
  EFI_IPv4_ADDRESS    SubnetMask;
  UINT16              StationPort;
  EFI_IPv4_ADDRESS    RemoteAddress;
  UINT16              RemotePort;
} EFI_UDP4_CONFIG_DATA;
  • TxTokensRxTokens:描述收发数据的映射:
typedef struct {
  LIST_ENTRY    Used;
  LIST_ENTRY    Recycled;
  UINTN         Count;
} NET_MAP;

真正的Token是EFI_UDP4_COMPLETION_TOKEN

typedef struct {
  EFI_EVENT     Event;
  EFI_STATUS    Status;
  union {
    EFI_UDP4_RECEIVE_DATA     *RxData;
    EFI_UDP4_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_UDP4_COMPLETION_TOKEN;
  • RcvdDgramQueDeliveredDgramQue:处理收发数据的队列。
  • IpInfo:底层IP4实例需要使用到的结构体:
///
/// The IP_IO_IP_INFO is used in IpIoSend() to override the default IP instance
/// in IP_IO.
///
typedef struct _IP_IO_IP_INFO {
  EFI_IP_ADDRESS               Addr;
  IP_IO_IP_MASK                PreMask;
  LIST_ENTRY                   Entry;
  EFI_HANDLE                   ChildHandle;
  IP_IO_IP_PROTOCOL            Ip;
  IP_IO_IP_COMPLETION_TOKEN    DummyRcvToken;
  INTN                         RefCnt;
  UINT8                        IpVersion;
} IP_IO_IP_INFO;

从注释中可以看到发送数据时会使用到。

EFI_UDP4_PROTOCOL

UDP4通信的接口,该Protocol的结构体如下:

///
/// The EFI_UDP4_PROTOCOL defines an EFI UDPv4 Protocol session that can be used
/// by any network drivers, applications, or daemons to transmit or receive UDP packets.
/// This protocol instance can either be bound to a specified port as a service or
/// connected to some remote peer as an active client. Each instance has its own settings,
/// such as the routing table and group table, which are independent from each other.
///
struct _EFI_UDP4_PROTOCOL {
  EFI_UDP4_GET_MODE_DATA    GetModeData;
  EFI_UDP4_CONFIGURE        Configure;
  EFI_UDP4_GROUPS           Groups;
  EFI_UDP4_ROUTES           Routes;
  EFI_UDP4_TRANSMIT         Transmit;
  EFI_UDP4_RECEIVE          Receive;
  EFI_UDP4_CANCEL           Cancel;
  EFI_UDP4_POLL             Poll;
};

对应的实现:

EFI_UDP4_PROTOCOL  mUdp4Protocol = {
  Udp4GetModeData,
  Udp4Configure,
  Udp4Groups,
  Udp4Routes,
  Udp4Transmit,
  Udp4Receive,
  Udp4Cancel,
  Udp4Poll
};

后面会介绍这些函数的实现。

Udp4.GetModeData

对应的实现是Udp4GetModeData(),其代码实现:

EFI_STATUS
EFIAPI
Udp4GetModeData (
  IN  EFI_UDP4_PROTOCOL                *This,
  OUT EFI_UDP4_CONFIG_DATA             *Udp4ConfigData OPTIONAL,
  OUT EFI_IP4_MODE_DATA                *Ip4ModeData    OPTIONAL,
  OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,
  OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL
  )
{
  if (Udp4ConfigData != NULL) {
    //
    // Set the Udp4ConfigData.
    //
    CopyMem (Udp4ConfigData, &Instance->ConfigData, sizeof (*Udp4ConfigData));
  }

  Ip = Instance->IpInfo->Ip.Ip4;

  //
  // Get the underlying Ip4ModeData, MnpConfigData and SnpModeData.
  //
  Status = Ip->GetModeData (Ip, Ip4ModeData, MnpConfigData, SnpModeData);

}

从这里可以看出,上层的网络协议可以获取到下层所有的模式数据。

对于UDP4来说,数据在UDP4_INSTANCE_DATAConfigData成员中。

Udp4.Configure

对应的实现是Udp4Configure(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Configure (
  IN EFI_UDP4_PROTOCOL     *This,
  IN EFI_UDP4_CONFIG_DATA  *UdpConfigData OPTIONAL
  )
{
  // 根据是否有配置存在两种情况,没有数据相当于重置
  if (UdpConfigData != NULL) {
    if (Instance->Configured) {
      //
      // The instance is already configured, try to do the re-configuration.
      //
      if (!Udp4IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {
        //
        // If the new configuration data wants to change some unreconfigurable
        // settings, return EFI_ALREADY_STARTED.
        //
        Status = EFI_ALREADY_STARTED;
        goto ON_EXIT;
      }

      //
      // Save the reconfigurable parameters.
      //
      Instance->ConfigData.TypeOfService   = UdpConfigData->TypeOfService;
      Instance->ConfigData.TimeToLive      = UdpConfigData->TimeToLive;
      Instance->ConfigData.DoNotFragment   = UdpConfigData->DoNotFragment;
      Instance->ConfigData.ReceiveTimeout  = UdpConfigData->ReceiveTimeout;
      Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;
    } else {
      //
      // Construct the Ip configuration data from the UdpConfigData.
      //
      Udp4BuildIp4ConfigData (UdpConfigData, &Ip4ConfigData);

      //
      // Configure the Ip instance wrapped in the IpInfo.
      //
      Status = IpIoConfigIp (Instance->IpInfo, &Ip4ConfigData);
      if (EFI_ERROR (Status)) {
        if (Status == EFI_NO_MAPPING) {
          Instance->IsNoMapping = TRUE;
        }

        goto ON_EXIT;
      }

      Instance->IsNoMapping = FALSE;

      //
      // Save the configuration data.
      //
      CopyMem (&Instance->ConfigData, UdpConfigData, sizeof (Instance->ConfigData));
      IP4_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip4ConfigData.StationAddress);
      IP4_COPY_ADDRESS (&Instance->ConfigData.SubnetMask, &Ip4ConfigData.SubnetMask);

      //
      // Try to allocate the required port resource.
      //
      Status = Udp4Bind (&Udp4Service->ChildrenList, &Instance->ConfigData);
      if (EFI_ERROR (Status)) {
        //
        // Reset the ip instance if bind fails.
        //
        IpIoConfigIp (Instance->IpInfo, NULL);
        goto ON_EXIT;
      }

      //
      // Pre calculate the checksum for the pseudo head, ignore the UDP length first.
      //
      CopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR));
      CopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR));
      Instance->HeadSum = NetPseudoHeadChecksum (
                            LocalAddr,
                            RemoteAddr,
                            EFI_IP_PROTO_UDP,
                            0
                            );

      Instance->Configured = TRUE;
    }
  } else {
    //
    // UdpConfigData is NULL, reset the instance.
    //
    Instance->Configured  = FALSE;
    Instance->IsNoMapping = FALSE;

    //
    // Reset the Ip instance wrapped in the IpInfo.
    //
    IpIoConfigIp (Instance->IpInfo, NULL);

    //
    // Cancel all the user tokens.
    //
    Instance->Udp4Proto.Cancel (&Instance->Udp4Proto, NULL);

    //
    // Remove the buffered RxData for this instance.
    //
    Udp4FlushRcvdDgram (Instance);
  }
}

根据输入参数的不同,以及是否已经配置过,会走到不同的流程,此外,UDP4还会配置进一步调用IP4的接口进行配置。

Udp4.Transmit

对应的实现是Udp4Transmit(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Transmit (
  IN EFI_UDP4_PROTOCOL          *This,
  IN EFI_UDP4_COMPLETION_TOKEN  *Token
  )
{
  //
  // Validate the Token, if the token is invalid return the error code.
  //
  Status = Udp4ValidateTxToken (Instance, Token);
  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)) ||
      EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token)))
  {
    //
    // Try to find a duplicate token in the two token maps, if found, return
    // EFI_ACCESS_DENIED.
    //
    Status = EFI_ACCESS_DENIED;
    goto ON_EXIT;
  }

  TxData = Token->Packet.TxData;

  //
  // Create a net buffer to hold the user buffer and the udp header.
  //
  Packet = NetbufFromExt (
             (NET_FRAGMENT *)TxData->FragmentTable,
             TxData->FragmentCount,
             UDP4_HEADER_SIZE,
             0,
             Udp4NetVectorExtFree,
             NULL
             );
  if (Packet == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Store the IpIo in ProtoData.
  //
  Udp4Service                       = Instance->Udp4Service;
  *((UINTN *)&Packet->ProtoData[0]) = (UINTN)(Udp4Service->IpIo);

  Udp4Header = (EFI_UDP_HEADER *)NetbufAllocSpace (Packet, UDP4_HEADER_SIZE, TRUE);
  ASSERT (Udp4Header != NULL);

  ConfigData = &Instance->ConfigData;

  //
  // Fill the udp header.
  //
  Udp4Header->SrcPort  = HTONS (ConfigData->StationPort);
  Udp4Header->DstPort  = HTONS (ConfigData->RemotePort);
  Udp4Header->Length   = HTONS ((UINT16)Packet->TotalSize);
  Udp4Header->Checksum = 0;

  UdpSessionData = TxData->UdpSessionData;
  IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &ConfigData->StationAddress);

  if (UdpSessionData != NULL) {
    //
    // Set the SourceAddress, SrcPort and Destination according to the specified
    // UdpSessionData.
    //
    if (!EFI_IP4_EQUAL (&UdpSessionData->SourceAddress, &mZeroIp4Addr)) {
      IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &UdpSessionData->SourceAddress);
    }

    if (UdpSessionData->SourcePort != 0) {
      Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);
    }

    if (UdpSessionData->DestinationPort != 0) {
      Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);
    }

    CopyMem (&Source, &Override.Ip4OverrideData.SourceAddress, sizeof (IP4_ADDR));
    CopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR));

    //
    // calculate the pseudo head checksum using the overridden parameters.
    //
    HeadSum = NetPseudoHeadChecksum (
                Source,
                Destination,
                EFI_IP_PROTO_UDP,
                0
                );
  } else {
    //
    // UdpSessionData is NULL, use the address and port information previously configured.
    //
    CopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR));

    HeadSum = Instance->HeadSum;
  }

  //
  // calculate the checksum.
  //
  Udp4Header->Checksum = Udp4Checksum (Packet, HeadSum);
  if (Udp4Header->Checksum == 0) {
    //
    // If the calculated checksum is 0, fill the Checksum field with all ones.
    //
    Udp4Header->Checksum = 0xffff;
  }

  //
  // Fill the IpIo Override data.
  //
  if (TxData->GatewayAddress != NULL) {
    IP4_COPY_ADDRESS (&Override.Ip4OverrideData.GatewayAddress, TxData->GatewayAddress);
  } else {
    ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
  }

  Override.Ip4OverrideData.Protocol      = EFI_IP_PROTO_UDP;
  Override.Ip4OverrideData.TypeOfService = ConfigData->TypeOfService;
  Override.Ip4OverrideData.TimeToLive    = ConfigData->TimeToLive;
  Override.Ip4OverrideData.DoNotFragment = ConfigData->DoNotFragment;

  //
  // Save the token into the TxToken map.
  //
  Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);

  //
  // Send out this datagram through IpIo.
  //
  IpDestAddr.Addr[0] = Destination;
  Status             = IpIoSend (
                         Udp4Service->IpIo,
                         Packet,
                         Instance->IpInfo,
                         Instance,
                         Token,
                         &IpDestAddr,
                         &Override
                         );
}

Udp4.Receive

对应的实现是Udp4Receive(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Receive (
  IN EFI_UDP4_PROTOCOL          *This,
  IN EFI_UDP4_COMPLETION_TOKEN  *Token
  )
{
  if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token)) ||
      EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)))
  {
    //
    // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or
    // RxTokens map.
    //
    Status = EFI_ACCESS_DENIED;
    goto ON_EXIT;
  }

  Token->Packet.RxData = NULL;

  //
  // Save the token into the RxTokens map.
  //
  Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);
  if (EFI_ERROR (Status)) {
    Status = EFI_NOT_READY;
    goto ON_EXIT;
  }

  //
  // If there is an icmp error, report it.
  //
  Udp4ReportIcmpError (Instance);

  //
  // Try to deliver the received datagrams.
  //
  Udp4InstanceDeliverDgram (Instance);

  //
  // Dispatch the DPC queued by the NotifyFunction of Token->Event.
  //
  DispatchDpc ();
}

同其它的网络协议中的Receive一样,重点是注册Token。

Udp4.Poll

对应的实现是Udp4Poll(),其代码实现就是调用下一层的Poll:

EFI_STATUS
EFIAPI
Udp4Poll (
  IN EFI_UDP4_PROTOCOL  *This
  )
{
  Ip       = Instance->IpInfo->Ip.Ip4;
  //
  // Invode the Ip instance consumed by the udp instance to do the poll operation.
  //
  return Ip->Poll (Ip);
}

代码示例

DHCP和DNS等都是使用UDP的,后面会进一步说明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/339355.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot3.2.2整合MyBatis Plus3.5.5

目录 1.前置条件 2.导坐标 3.配置数据源 4.配置mapper扫描路径 5.MyBatis Plus代码生成器整合 1.导坐标 2.编写代码生成逻辑 1.前置条件 已经初始化好一个spring boot项目且版本为3X&#xff0c;项目可正常启动 2.导坐标 <dependency><groupId>com.baomid…

《WebKit 技术内幕》之六(1): CSS解释器和样式布局

《WebKit 技术内幕》之六&#xff08;1&#xff09;&#xff1a;CSS解释器和样式布局 CSS解释器和规则匹配处于DOM树建立之后&#xff0c;RenderObject树之前&#xff0c;CSS解释器解释后的结果会保存起来&#xff0c;然后RenderObject树基于该结果来进行规范匹配和布局计算。当…

NodeJs 第二十章 代理

在计算机网络中&#xff0c;代理是一种中间服务&#xff0c;能够代理用户与网络资源之间的通信。代理服务器可以缓存网页内容、过滤网络流量或隐藏用户的真实IP地址等功能。 在日常开发中&#xff0c;我们接触最多的是客户端发送ajax到服务端。但是服务端并不是 node &#xf…

搭建DNS 服务

安装DNS 服务 [rootlocalhost ~]# yum install bind -y Complete! [rootlocalhost ~]# DNS 配置文件 [rootlocalhost ~]# vim /etc/named.conf [rootlocalhost ~]# cat -n /etc/named.conf 1 // ... 12 options { 13 #listen-on port 53 { 127.0.0.1; }; 14 list…

【蓝桥杯冲冲冲】动态规划初步[USACO2006 OPEN] 县集市

蓝桥杯备赛 | 洛谷做题打卡day13 文章目录 蓝桥杯备赛 | 洛谷做题打卡day13题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示样例说明数据规模与约定 思路&#xff1a;方程&#xff1a; 题解代码我的一些话 [USACO2006 OPEN] 县集市 The County Fair 题目描述 每年…

说说你对归并排序的理解?如何实现?应用场景?

一、是什么 归并排序&#xff08;Merge Sort&#xff09;是建立归并操作上的一种有效&#xff0c;稳定的排序算法&#xff0c;该算法是采用分治法的一个非常典型的应用 将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序&#xff0c;再使…

几个简单好用Python库,让你工作效率翻倍

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python是一门强大的编程语言&#xff0c;不仅可以进行软件开发&#xff0c;还可以通过各种优秀的第三方库来提高工作效率。本文将介绍几个简单而好用的Python库&#xff0c;它们可以帮助你在各种领域提高工作效率…

02不吉利日期,noi练习题,小学生编程

02:不吉利日期 总时间限制: 1000ms 内存限制: 65536kB 描述 在国外&#xff0c;每月的13号和每周的星期5都是不吉利的。特别是当13号那天恰好是星期5时&#xff0c;更不吉利。已知某年的一月一日是星期w&#xff0c;并且这一年一定不是闰年&#xff0c;求出这一年所有13号…

设计模式—行为型模式之观察者模式

设计模式—行为型模式之观察者模式 观察者模式(Observer Pattern)&#xff1a;定义对象间的一种一对多依赖关系&#xff0c;使得每当一个对象状态发生改变时&#xff0c;其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅&#xff08;Publish/Subscribe&#…

Qt应用开发(安卓篇)——Hello Qt On Android

一、前言 这一篇从实际出发&#xff0c;讲述如何创建、编译和部署Qt On Android项目。 二、ADB调试 ADB的全称为Android Debug Bridge&#xff0c;就是起到调试桥的作用&#xff0c;主要用于连接计算机与Android 设备&#xff0c;以便进行调试和数据传输。ADB 可以实现以下主要…

如何使用CRM实现销售流程自动化?CRM如何提高销售效率?

科技在当今时代扮演着重要的角色。在商业领域&#xff0c;我们用很多不同的软件来完成业务、提高效率。销售被认为是一个企业的灵魂。没有销售&#xff0c;企业很难生存。为了使销售更加有效&#xff0c;自动化是每个企业都应该采用的一个重要战略。实现销售过程自动化最简单的…

【视频媒体】深入了解直播视频流

深入了解直播视频流&#x1f3a5; YouTube、TikTok live和Twitch上的直播视频是如何工作的&#xff1f; 直播视频流与常规流媒体不同&#xff0c;因为视频内容通过互联网近乎实时发送&#xff0c;通常只有几秒钟的延迟。 下图解释了实现这一目标背后所发生的事情。 步骤1&…

【QT+QGIS跨平台编译】之四:【libSSH2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libSSH2介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、libSSH2介绍 libSSH2是一个开源的C函数库&#xff0c;用来实现SSH2协议。 SSH(Secure SHell)到目前为止有两个不兼容的版本——SSH1和SSH2。 SSH2避免了RSA的专利问题&#xff0c;并修补了CRC…

HR人才测评,招聘技术部经理的胜任力素质模型与任职资格

招聘技术部经理是企业招聘中的关键职位之一。为了确保招聘到胜任的人才&#xff0c;需要制定一个详细的胜任力素质模型和任职资格要求。下面将详细说明。 企业版团测 - 在线人才测评系统、人才测评工具、人才盘点、团队测评、心理测评、职业测评 - 在线工具网团测&#xff0c;…

Python爬虫--5

1、异步爬虫 异步爬虫的方式&#xff1a; &#xff08;1&#xff09;多线程&#xff0c;多进程&#xff08;不建议使用&#xff09; 好处&#xff1a;可以为相关阻塞的操作单独开启线程或者进程&#xff0c;阻塞操作就可以异步执行。 弊端&#xff1a;无法无限制的开启多线程…

3.php开发-个人博客项目输入输出类留言板访问IPUA头来源

目录 知识点 : 输入输出 配置环境时&#xff1a; 搜索框&#xff1a; 留言板&#xff1a; 留言板的显示&#xff08;html&#xff09;&#xff1a; php代码显示提交的留言&#xff1a; 写入数据库 对留言内容进行显示&#xff1a; php全局变量-$_SERVER 检测来源 墨…

1.11马原总复习PART2

社会一定发展阶段 &#xff0c;生产力&#xff0c;生产关系总和 一定经济基础之上的意识形态&#xff0c;制度、组织和设施 普遍联系的根本内容和变化发展的内在动力 唯物辩证法的实质和核心 贯穿其他规律的中心线索 提供了根本方法矛盾分析法 价值由社会必要劳动时间决定…

安装ddddocr中遇到的问题

1、需要先安装&#xff1a; pip3 install pyinstaller --no-use-pep517 pip install scikit-build pip install setuptools pip install pyinstaller pip install pillow 重要是的是保证一个python 环境&#xff0c;多个python环境会导致各种问题。并且保证python>3.8…

LTC2944库仑计(电量计)芯片应用笔记(Arduino,ESP32)

一、一些基础知识 1.蓄电池的容量单位 &#xff08;1&#xff09;毫安时mAH 蓄电池的容量一般会采用毫安时&#xff08;mAH&#xff09;为单位&#xff0c;比如2000mAH的蓄电池意思是该蓄电池理论上可以以2000mA的电流持续放电1小时&#xff0c;2000mA*1H2000mAH。当然这个是…

C++从小白到初级工程师【个人学习笔记】

目录 1.背景2.基础二维数组概念二维数组定义方式 二维数组数组名称概念例子 函数的分文件编写概念示例 指针指针的基本概念指针变量的定义和使用 空指针和野指针空指针实例野指针实例 const修饰指针概念const修饰指针 --- 常量指针 指针和数组作用示例 指针和函数作用示例 指针…