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

DHCP4

DHCP4协议说明

DHCP是应用层的协议,DHCP报文是承载UDP上的高层协议报文,采用67(DHCP服务器)和68(DHCP客户端)两个端口号。

DHCP的全称是Dynamic Host Configuration Protocol,它的主要功能是实现自动设置IP地址、统一管理IP地址分管。

在这里插入图片描述

其工作原理:

在这里插入图片描述

DHCP报文格式如下:

在这里插入图片描述

各个参数的说明如下:

字段长度(字节)含义
Op1表示报文的类型:
1:客户端请求报文
2:服务器响应报文
Htype1表示硬件地址的类型。对于以太网,该类型的值为“1”。
Hlen1表示硬件地址的长度,单位是字节。对于以太网,该值为6。
Hops1跳数。客户端设置为0,也能被一个代理服务器设置。
Xid4事务ID,由客户端选择的一个随机数,被服务器和客户端用来在它们之间交流请求和响应,客户端用它对请求和应答进行匹配。
该ID由客户端设置并由服务器返回,为32位整数。
Secs2由客户端填充,表示从客户端开始获得IP地址或IP地址续借后所使用了的秒数。
Flags2此字段在BOOTP中保留未用,在DHCP中表示标志字段。
Flags字段格式:
0 15
+---------------------------------+
`
Ciaddr4客户端的IP地址。只有客户端是Bound、Renew、Rebinding状态,并且能响应ARP请求时,才能被填充。
Yiaddr4“你自己的”或客户端的IP地址。
Siaddr4表明DHCP协议流程的下一个阶段要使用的服务器的IP地址。
Giaddr4该字段表示第一个DHCP中继的IP地址(注意:不是地址池中定义的网关)。
当客户端发出DHCP请求时,如果服务器和客户端不在同一个网络中,那么第一个DHCP中继在转发这个DHCP请求报文时会把自己的IP地址填入此字段。
服务器会根据此字段来判断出网段地址,从而选择为用户分配地址的地址池。
服务器还会根据此地址将响应报文发送给此DHCP中继,再由DHCP中继将此报文转发给客户端。
若在到达DHCP服务器前经过了不止一个DHCP中继,那么第一个DHCP中继后的中继不会改变此字段,只是把Hops的数目加1。
Chaddr16该字段表示客户端的MAC地址,此字段与前面的“Hardware Type”和“Hardware Length”保持一致。当客户端发出DHCP请求时,将自己的硬件地址填入此字段。对于以太网,当“Hardware Type”和“Hardware Length”分别为“1”和“6”时,此字段必须填入6字节的以太网MAC地址。
Sname64该字段表示客户端获取配置信息的服务器名字。此字段由DHCP Server填写,是可选的。
如果填写,必须是一个以0结尾的字符串。
File128该字段表示客户端的启动配置文件名。此字段由DHCP Server填写,是可选的。
如果填写,必须是一个以0结尾的字符串。
Options可变该字段表示DHCP的选项字段,至少为312字节,格式为“代码+长度+数据”。
DHCP通过此字段包含了服务器分配给终端的配置信息,如网关IP地址,DNS服务器的IP地址,客户端可以使用IP地址的有效租期等信息。

对应到代码中的结构体:

#pragma pack(1)
///
/// EFI_DHCP4_PACKET defines the format of DHCPv4 packets. See RFC 2131 for more information.
///
typedef struct {
  UINT8               OpCode;
  UINT8               HwType;
  UINT8               HwAddrLen;
  UINT8               Hops;
  UINT32              Xid;
  UINT16              Seconds;
  UINT16              Reserved;
  EFI_IPv4_ADDRESS    ClientAddr;       ///< Client IP address from client.
  EFI_IPv4_ADDRESS    YourAddr;         ///< Client IP address from server.
  EFI_IPv4_ADDRESS    ServerAddr;       ///< IP address of next server in bootstrap.
  EFI_IPv4_ADDRESS    GatewayAddr;      ///< Relay agent IP address.
  UINT8               ClientHwAddr[16]; ///< Client hardware address.
  CHAR8               ServerName[64];
  CHAR8               BootFileName[128];
} EFI_DHCP4_HEADER;
#pragma pack()

DHCP4代码综述

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

EFI_STATUS
EFIAPI
Dhcp4DriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EfiLibInstallDriverBindingComponentName2 (
           ImageHandle,
           SystemTable,
           &gDhcp4DriverBinding,
           ImageHandle,
           &gDhcp4ComponentName,
           &gDhcp4ComponentName2
           );
}

仅仅是安装了gDhcp4DriverBinding

EFI_DRIVER_BINDING_PROTOCOL  gDhcp4DriverBinding = {
  Dhcp4DriverBindingSupported,
  Dhcp4DriverBindingStart,
  Dhcp4DriverBindingStop,
  0xa,
  NULL,
  NULL
};

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

支持
提供
支持
支持
提供
支持
提供
提供
提供
支持
提供
提供
支持
支持
提供
提供
提供
支持
提供
提供
gEfiPciIoProtocolGuid
UNDI
gEfiNetworkInterfaceIdentifierProtocolGuid_31
gEfiDevicePathProtocolGuid
SNP
gEfiSimpleNetworkProtocolGuid
MNP
gEfiVlanConfigProtocolGuid
gEfiManagedNetworkServiceBindingProtocolGuid
gEfiManagedNetworkProtocolGuid
ARP
gEfiArpServiceBindingProtocolGuid
gEfiArpProtocolGuid
IP4
gEfiIp4ServiceBindingProtocolGuid
gEfiIp4Config2ProtocolGuid
gEfiIp4ProtocolGuid
DHCP4
gEfiDhcp4ServiceBindingProtocolGuid
gEfiDhcp4ProtocolGuid

它跟DNS4的结构基本一致。

Dhcp4DriverBindingSupported

DHCP4依赖于UDP4

EFI_STATUS
EFIAPI
Dhcp4DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUdp4ServiceBindingProtocolGuid,
                  NULL,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                  );
}

Dhcp4DriverBindingStart

Start函数的流程大致如下:

  1. 创建DHCP_SERVICE,后面会进一步介绍。
  2. 通过UDP4来接收数据,对应的函数是UdpIoRecvDatagram(),该函数会循环执行:
EFI_STATUS
EFIAPI
UdpIoRecvDatagram (
  IN  UDP_IO           *UdpIo,
  IN  UDP_IO_CALLBACK  CallBack,
  IN  VOID             *Context,
  IN  UINT32           HeadLen
  )
{
  RxToken = UdpIoCreateRxToken (UdpIo, CallBack, Context, HeadLen);

  UdpIo->RecvRequest = RxToken;
  if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
    Status = UdpIo->Protocol.Udp4->Receive (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
  }
}

因为Start函数中有如下的代码:

  //
  // Start the receiving
  //
  Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);

所以UdpIoRecvDatagram()中创建的Token对应的回调函数是DhcpInput(),而该函数的实现中有:

VOID
EFIAPI
DhcpInput (
  NET_BUF        *UdpPacket,
  UDP_END_POINT  *EndPoint,
  EFI_STATUS     IoStatus,
  VOID           *Context
  )
{
  // 前面是数据处理,处理完之后就跳转到RESTRT,或者就走这里的异常,但是也是调用了UdpIoRecvDatagram()
  if (EFI_ERROR (Status)) {
    NetbufFree (UdpPacket);
    UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
    DhcpEndSession (DhcpSb, Status);
    return;
  }
    
RESTART:
  Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
}

所以在注释中才有“Start the receiving”的说法。

  1. 安装gEfiDhcp4ServiceBindingProtocolGuid

DHCP_SERVICE

DHCP_SERVICE在Start函数中创建:

EFI_STATUS
EFIAPI
Dhcp4DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);
}

其结构体位于NetworkPkg\Dhcp4Dxe\Dhcp4Impl.h:

//
// DHCP driver is specical in that it is a singleton. Although it
// has a service binding, there can be only one active child.
//
struct _DHCP_SERVICE {
  UINT32                          Signature;
  EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;

  INTN                            ServiceState; // CONFIGED, UNCONFIGED, and DESTROY

  EFI_HANDLE                      Controller;
  EFI_HANDLE                      Image;

  LIST_ENTRY                      Children;
  UINTN                           NumChildren;

  INTN                            DhcpState;
  EFI_STATUS                      IoStatus;   // the result of last user operation
  UINT32                          Xid;

  IP4_ADDR                        ClientAddr; // lease IP or configured client address
  IP4_ADDR                        Netmask;
  IP4_ADDR                        ServerAddr;

  EFI_DHCP4_PACKET                *LastOffer; // The last received offer
  EFI_DHCP4_PACKET                *Selected;
  DHCP_PARAMETER                  *Para;

  UINT32                          Lease;
  UINT32                          T1;
  UINT32                          T2;
  INTN                            ExtraRefresh; // This refresh is reqested by user

  UDP_IO                          *UdpIo;       // Udp child receiving all DHCP message
  UDP_IO                          *LeaseIoPort; // Udp child with lease IP
  EFI_DHCP4_PACKET                *LastPacket;  // The last sent packet for retransmission
  EFI_MAC_ADDRESS                 Mac;
  UINT8                           HwType;
  UINT8                           HwLen;
  UINT8                           ClientAddressSendOut[16];

  DHCP_PROTOCOL                   *ActiveChild;
  EFI_DHCP4_CONFIG_DATA           ActiveConfig;
  UINT32                          UserOptionLen;

  //
  // Timer event and various timer
  //
  EFI_EVENT                       Timer;

  UINT32                          PacketToLive; // Retransmission timer for our packets
  UINT32                          LastTimeout;  // Record the init value of PacketToLive every time
  INTN                            CurRetry;
  INTN                            MaxRetries;
  UINT32                          LeaseLife;
};

这里首先需要注意开头的注释,虽然可以由多个这样的服务数据,但是DHCP4只是一个单例(singleton),也就是有效的只有一个。

重要参数说明如下:

  • ServiceState:对应的值:
//
// The state of the DHCP service. It starts as UNCONFIGED. If
// and active child configures the service successfully, it
// goes to CONFIGED. If the active child configures NULL, it
// goes back to UNCONFIGED. It becomes DESTROY if it is (partly)
// destroyed.
//
#define DHCP_UNCONFIGED  0
#define DHCP_CONFIGED    1
#define DHCP_DESTROY     2

关于它的使用情况已经在上述注释总说明。

  • DhcpState:它表示的是通信过程中的DHCP状态,对应的值:
typedef enum {
  ///
  /// The EFI DHCPv4 Protocol driver is stopped.
  ///
  Dhcp4Stopped = 0x0,
  ///
  /// The EFI DHCPv4 Protocol driver is inactive.
  ///
  Dhcp4Init = 0x1,
  ///
  /// The EFI DHCPv4 Protocol driver is collecting DHCP offer packets from DHCP servers.
  ///
  Dhcp4Selecting = 0x2,
  ///
  /// The EFI DHCPv4 Protocol driver has sent the request to the DHCP server and is waiting for a response.
  ///
  Dhcp4Requesting = 0x3,
  ///
  /// The DHCP configuration has completed.
  ///
  Dhcp4Bound = 0x4,
  ///
  /// The DHCP configuration is being renewed and another request has
  /// been sent out, but it has not received a response from the server yet.
  ///
  Dhcp4Renewing = 0x5,
  ///
  /// The DHCP configuration has timed out and the EFI DHCPv4
  /// Protocol driver is trying to extend the lease time.
  ///
  Dhcp4Rebinding = 0x6,
  ///
  /// The EFI DHCPv4 Protocol driver was initialized with a previously
  /// allocated or known IP address.
  ///
  Dhcp4InitReboot = 0x7,
  ///
  /// The EFI DHCPv4 Protocol driver is seeking to reuse the previously
  /// allocated IP address by sending a request to the DHCP server.
  ///
  Dhcp4Rebooting = 0x8
} EFI_DHCP4_STATE;
  • IoStatus:表示的是DHCP通信操作导致的状态。
  • Xid:一个随机数,在DHCP4协议说明中已经有介绍。
  • ClientAddrNetmaskServerAddrClientAddr是通过DHCP租用到的IP地址,对应代码:
EFI_STATUS
DhcpLeaseAcquired (
  IN OUT DHCP_SERVICE  *DhcpSb
  )
{
  DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);

  if (DhcpSb->Para != NULL) {
    DhcpSb->Netmask    = DhcpSb->Para->NetMask;
    DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
  }
}

另外两个IP也在这里设置。

  • LastOffer:最后一次收到的包。
  • Selected:从中获取IP的那个包。
  • Para:DHCP参数,其结构体如下:
///
/// The options that matters to DHCP driver itself. The user of
/// DHCP clients may be interested in other options, such as
/// classless route, who can parse the DHCP offer to get them.
///
typedef struct {
  IP4_ADDR    NetMask;                // DHCP4_TAG_NETMASK
  IP4_ADDR    Router;                 // DHCP4_TAG_ROUTER, only the first router is used

  //
  // DHCP specific options
  //
  UINT8       DhcpType;               // DHCP4_TAG_MSG_TYPE
  UINT8       Overload;               // DHCP4_TAG_OVERLOAD
  IP4_ADDR    ServerId;               // DHCP4_TAG_SERVER_ID
  UINT32      Lease;                  // DHCP4_TAG_LEASE
  UINT32      T1;                     // DHCP4_TAG_T1
  UINT32      T2;                     // DHCP4_TAG_T2
} DHCP_PARAMETER;

LeaseT1T2等参数也在这里有体现。

  • ExtraRefresh:更新租用期时会使用到,对应到函数:
/**
  Extends the lease time by sending a request packet.

  The RenewRebind() function is used to manually extend the lease time when the
  EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
  not expired yet. This function will send a request packet to the previously
  found server (or to any server when RebindRequest is TRUE) and transfer the
  state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
  TRUE). When a response is received, the state is returned to Dhcp4Bound.
  If no response is received before the try count is exceeded (the RequestTryCount
  field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
  was issued by the previous server expires, the driver will return to the Dhcp4Bound
  state and the previous configuration is restored. The outgoing and incoming packets
  can be captured by the EFI_DHCP4_CALLBACK function.

  @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
  @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
                              the Dhcp4Rebinding state. Otherwise, it sends a unicast
                              request packet and enters the Dhcp4Renewing state.
  @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
                              completes or some error occurs.
                              EFI_DHCP4_PROTOCOL.GetModeData() can be called to
                              check the completion status. If NULL,
                              EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
                              until the DHCP process finishes.

  @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
                                Dhcp4Renewing state or is back to the Dhcp4Bound state.
  @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
                                state. EFI_DHCP4_PROTOCOL.Configure() needs to
                                be called.
  @retval EFI_INVALID_PARAMETER This is NULL.
  @retval EFI_TIMEOUT           There was no response from the server when the try count was
                                exceeded.
  @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

**/
EFI_STATUS
EFIAPI
EfiDhcp4RenewRebind (
  IN EFI_DHCP4_PROTOCOL  *This,
  IN BOOLEAN             RebindRequest,
  IN EFI_EVENT           CompletionEvent   OPTIONAL
  )
{
  DhcpSb->ExtraRefresh       = TRUE;
}
  • UdpIoUDP4通信接口,这里包装了一层,因为有v4和v6两个版本,我们只关注v4版本。
  • LeaseIoPort:也是UDP4通信接口,不过对应到获取租用IP的接口。
  • LastPacket:最后发送的包。
  • MacHwTypeHwLen:网卡参数,对应到EFI_SIMPLE_NETWORK_MODE中的值。
  • ClientAddressSendOut:对应DHCP包头部中的ClientHwAddr
  • ActiveChildActiveConfig:前面已经说过DHCP使用单例,所以对应的DHCP_PROTOCOL也只有一个是有效的,这个就执行有效的DHCP_PROTOCOL,以及对应的参数。
  • UserOptionLen:DHCP选项的长度。
  • Timer:一个定时器,在创建服务时创建:
  //
  // Create various resources, UdpIo, Timer, and get Mac address
  //
  Status = gBS->CreateEvent (
                  EVT_NOTIFY_SIGNAL | EVT_TIMER,
                  TPL_CALLBACK,
                  DhcpOnTimerTick,
                  DhcpSb,
                  &DhcpSb->Timer
                  );

从对应的回调函数中可以知道它的作用:

/**
  Each DHCP service has three timer. Two of them are count down timer.
  One for the packet retransmission. The other is to collect the offers.
  The third timer increments the lease life which is compared to T1, T2,
  and lease to determine the time to renew and rebind the lease.
  DhcpOnTimerTick will be called once every second.

  @param[in]  Event                 The timer event
  @param[in]  Context               The context, which is the DHCP service instance.

**/
VOID
EFIAPI
DhcpOnTimerTick (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )

它每秒执行一次:

  Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
  • PacketToLiveLastTimeoutCurRetryMaxRetries:与定时器相关的一些值,还与重发等机制相关。
  • LeaseLife:租用时间。
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值