网站推广技术哪家好推广网app下载

当前位置: 首页 > news >正文

网站推广技术哪家好,推广网app下载,网站订单系统模板,招生门户网站建设方案ARP ARP协议说明 从这里开始涉及到的网络协议都是比较通用的了#xff0c;在一般的TCP/IP四层模型中都能够看到这些内容#xff0c;不过这里主要介绍的还是其在BIOS下的实现#xff0c;但是在此之前还是需要先说明ARP的作用。 ARP的全称是Address Resolution Protocol在一般的TCP/IP四层模型中都能够看到这些内容不过这里主要介绍的还是其在BIOS下的实现但是在此之前还是需要先说明ARP的作用。 ARP的全称是Address Resolution Protocol它是一种解决地址问题的协议。以目标IP为线索用来定义下一个应该接收数据分包的网络设备对应的MAC地址。ARP只用于IPv4不能用于IPv6IPv6使用ICMPv6替代ARP。ARP获取MAC地址的简单流程如下 ARP是请求方IP通过广播发送的请求包同一链路上所有的主机和路由器都会接收到这个包目标地址将自己的MAC地址填入到ARP响应包返回给请求方IP。一个ARP包的格式如下 各个参数的说明如下 字段长度bit含义Ethernet Address of Destination48目的MAC地址。发送ARP请求时为广播的MAC地址FF-FF-FF-FF-FF-FF。Ethernet Address of Sender48源MAC地址。Frame Type16表示后面数据的类型。对于ARP请求或应答来说该字段的值为0x0806。Hardware Type16表示硬件地址的类型。对于以太网该类型的值为“1”。Protocol Type16表示发送方要映射的协议地址类型。对于IP地址该值为0x0800。Hardware Length8表示硬件地址的长度单位是字节。对于ARP请求或应答来说该值为6。Protocol Length8表示协议地址的长度单位是字节。对于ARP请求或应答来说该值为4。OP16操作类型:1ARP请求2ARP应答3RARP请求4RARP应答Ethernet Address of Sender48发送方以太网地址。这个字段和ARP报文首部的源以太网地址字段是重复信息。IP Address of Sender32发送方的IP地址。Ethernet Address of Destination48接收方的以太网地址。发送ARP请求时该处填充值为00-00-00-00-00-00。IP Address of Destination32接收方的IP地址。 ARP包在UEFI代码中没有一个特定的结构体来表示不过其中的一部分还是构成了结构体 // // ARP packet head definition. // #pragma pack(1) typedef struct {UINT16 HwType;UINT16 ProtoType;UINT8 HwAddrLen;UINT8 ProtoAddrLen;UINT16 OpCode; } ARP_HEAD; #pragma pack()而整个ARP包的构造则位于ArpSendFrame函数中。 ARP代码综述 ARP的实现代码位于NetworkPkg\ArpDxe\ArpDxe.inf它也是一个UEFI Driver Model所以会安装EFI_DRIVER_BINDING_PROTOCOL其实现如下 EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding {ArpDriverBindingSupported,ArpDriverBindingStart,ArpDriverBindingStop,0xa,NULL,NULL };ARP在UEFI网络协议栈中的关系图 #mermaid-svg-VU5ylXNSqCLnn3Ky {font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .error-icon{fill:#552222;}#mermaid-svg-VU5ylXNSqCLnn3Ky .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VU5ylXNSqCLnn3Ky .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .marker.cross{stroke:#333333;}#mermaid-svg-VU5ylXNSqCLnn3Ky svg{font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .label{font-family:“trebuchet ms”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .cluster-label text{fill:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .cluster-label span{color:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .label text,#mermaid-svg-VU5ylXNSqCLnn3Ky span{fill:#333;color:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .node rect,#mermaid-svg-VU5ylXNSqCLnn3Ky .node circle,#mermaid-svg-VU5ylXNSqCLnn3Ky .node ellipse,#mermaid-svg-VU5ylXNSqCLnn3Ky .node polygon,#mermaid-svg-VU5ylXNSqCLnn3Ky .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .node .label{text-align:center;}#mermaid-svg-VU5ylXNSqCLnn3Ky .node.clickable{cursor:pointer;}#mermaid-svg-VU5ylXNSqCLnn3Ky .arrowheadPath{fill:#333333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VU5ylXNSqCLnn3Ky .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VU5ylXNSqCLnn3Ky .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VU5ylXNSqCLnn3Ky .cluster text{fill:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky .cluster span{color:#333;}#mermaid-svg-VU5ylXNSqCLnn3Ky div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VU5ylXNSqCLnn3Ky :root{–mermaid-font-family:“trebuchet ms”,verdana,arial,sans-serif;}#mermaid-svg-VU5ylXNSqCLnn3Ky .default*{fill:#bbb!important;stroke:#333!important;stroke-width:2px!important;}#mermaid-svg-VU5ylXNSqCLnn3Ky .default span{fill:#bbb!important;stroke:#333!important;stroke-width:2px!important;} 支持 提供 支持 支持 提供 支持 提供 提供 提供 支持 提供 提供 gEfiPciIoProtocolGuid UNDI gEfiNetworkInterfaceIdentifierProtocolGuid_31 gEfiDevicePathProtocolGuid SNP gEfiSimpleNetworkProtocolGuid MNP gEfiVlanConfigProtocolGuid gEfiManagedNetworkServiceBindingProtocolGuid gEfiManagedNetworkProtocolGuid ARP gEfiArpServiceBindingProtocolGuid gEfiArpProtocolGuid ArpDriverBindingSupported ARP依赖于MNP所以其Supported函数实现主体如下 EFI_STATUS EFIAPI ArpDriverBindingSupported (IN EFI_DRIVER_BINDING_PROTOCOL *This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL) {//// Test to see if MNP SB is installed.//Status gBS-OpenProtocol (ControllerHandle,gEfiManagedNetworkServiceBindingProtocolGuid,NULL,This-DriverBindingHandle,ControllerHandle,EFI_OPEN_PROTOCOL_TEST_PROTOCOL); }也只是一个简单的MNP是否已经支持的判断。 ArpDriverBindingStart Start函数的执行流程大致如下 使用ArpCreateService()函数初始化ARP_SERVICE_DATA。安装gEfiArpServiceBindingProtocolGuid对应的服务Protocol跟MNP的是一样的 struct _EFI_SERVICE_BINDING_PROTOCOL {EFI_SERVICE_BINDING_CREATE_CHILD CreateChild;EFI_SERVICE_BINDING_DESTROY_CHILD DestroyChild; };通过MNP接口注册Token。 //// OK, start to receive arp packets from Mnp.//Status ArpService-Mnp-Receive (ArpService-Mnp, ArpService-RxToken);这里注册的Token还是在第1步中初始化的所以以上的所有操作中最重要的还是初始化ARP_SERVICE_DATA的操作后续将进一步介绍该结构体。注意ARP中没有像MNP那样的MNP_DEVICE_DATA这是因为ARP已经跟硬件没有关系但是ARP中也有服务数据和实例数据分别对应ARP_SERVICE_DATA和ARP_INSTANCE_DATA。ARP中的主要数据以及它们的关系如下图所示 ARP_SERVICE_DATA ARP_SERVICE_DATA结构体位于NetworkPkg\ArpDxe\ArpImpl.h其实现如下 // // ARP service data structure. // struct _ARP_SERVICE_DATA {UINT32 Signature;EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;EFI_HANDLE MnpChildHandle;EFI_HANDLE ImageHandle;EFI_HANDLE ControllerHandle;EFI_MANAGED_NETWORK_PROTOCOL *Mnp;EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;EFI_MANAGED_NETWORK_COMPLETION_TOKEN RxToken;EFI_SIMPLE_NETWORK_MODE SnpMode;UINTN ChildrenNumber;LIST_ENTRY ChildrenList;LIST_ENTRY PendingRequestTable;LIST_ENTRY DeniedCacheTable;LIST_ENTRY ResolvedCacheTable;EFI_EVENT PeriodicTimer; };该结构体的初始化在ArpCreateService()函数中除了初始化ARP_SERVICE_DATA之外还有一个很重要的代码是 //// Create a MNP child instance.//Status NetLibCreateServiceChild (ControllerHandle,ImageHandle,gEfiManagedNetworkServiceBindingProtocolGuid,ArpService-MnpChildHandle);完成这一步操作之后MNP服务才会创建子项才会安装EFI_MANAGED_NETWORK_PROTOCOL供后续ARP使用。 下面介绍其中比较重要的成员 ServiceBinding对应ARP的服务Protocol由于APR依赖的是MNP的gEfiManagedNetworkServiceBindingProtocolGuid而MNP中可以有多个服务因此ARP中也可能有多个。对应的实现函数 //// Init the servicebinding protocol members.//ArpService-ServiceBinding.CreateChild ArpServiceBindingCreateChild;ArpService-ServiceBinding.DestroyChild ArpServiceBindingDestroyChild;MnpChildHandle、Mnp对应MNP中的EFI_MANAGED_NETWORK_PROTOCOL及其所在的Handle这个Handle上面还有gEfiManagedNetworkServiceBindingProtocolGuid对应的服务Protocol。MnpConfigDataMNP的配置参数其值是固定的 //// Set the Mnp config parameters.//ArpService-MnpConfigData.ReceivedQueueTimeoutValue 0;ArpService-MnpConfigData.TransmitQueueTimeoutValue 0;ArpService-MnpConfigData.ProtocolTypeFilter ARP_ETHER_PROTO_TYPE;ArpService-MnpConfigData.EnableUnicastReceive TRUE;ArpService-MnpConfigData.EnableMulticastReceive FALSE;ArpService-MnpConfigData.EnableBroadcastReceive TRUE;ArpService-MnpConfigData.EnablePromiscuousReceive FALSE;ArpService-MnpConfigData.FlushQueuesOnReset TRUE;ArpService-MnpConfigData.EnableReceiveTimestamps FALSE;ArpService-MnpConfigData.DisableBackgroundPolling FALSE;之后会用这些值来配置一次MNP //// Configure the Mnp child.//Status ArpService-Mnp-Configure (ArpService-Mnp, ArpService-MnpConfigData);这一点很重要因为MNP在接收到数据之后会根据这些值来确定是否需要回调ARP的处理函数。 RxToken包含了ARP对MNP接收到的数据的处理对应的处理函数是ArpOnFrameRcvd()它实际上包含了ARP模块的主要功能。 ChildrenList、ChildrenNumberArpServiceBindingCreateChild()创建的ARP子项的链表。 PendingRequestTable处理ARP的重试。 DeniedCacheTable、ResolvedCacheTableARP缓存表用来缓存IP-MAC的对应关系避免需要一直使用ARP来获取指定IP对应的MAC地址。 PeriodicTimer处理ARP心跳的定时事件 //// Create the Arp heartbeat timer.//Status gBS-CreateEvent (EVT_NOTIFY_SIGNAL | EVT_TIMER,TPL_CALLBACK,ArpTimerHandler,ArpService,ArpService-PeriodicTimer);它的主要作用就是ARP包的重试和缓存处理后面将进一步介绍。 ARP_INSTANCE_DATA ARP_INSTANCE_DATA结构体位于NetworkPkg\ArpDxe\ArpImpl.h其结构体如下 // // ARP instance context data structure. // typedef struct {UINT32 Signature;ARP_SERVICE_DATA *ArpService;EFI_HANDLE Handle;EFI_ARP_PROTOCOL ArpProto;LIST_ENTRY List;EFI_ARP_CONFIG_DATA ConfigData;BOOLEAN Configured;BOOLEAN InDestroy; } ARP_INSTANCE_DATA;它在ARP服务创建ARP子项的时候生成对应的接口是ArpService-ServiceBinding.CreateChild()初始化在函数ArpInitInstance()中 VOID ArpInitInstance (IN ARP_SERVICE_DATA *ArpService,OUT ARP_INSTANCE_DATA *Instance) {NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);Instance-Signature ARP_INSTANCE_DATA_SIGNATURE;Instance-ArpService ArpService;CopyMem (Instance-ArpProto, mEfiArpProtocolTemplate, sizeof (Instance-ArpProto));Instance-Configured FALSE;Instance-InDestroy FALSE;InitializeListHead (Instance-List); }其中比较重要的成员有 ArpService创建子项的那个服务对应的数据。 ArpProtoARP操作接口
// // Global variable of EFI ARP Protocol Interface. // EFI_ARP_PROTOCOL mEfiArpProtocolTemplate {ArpConfigure,ArpAdd,ArpFind,ArpDelete,ArpFlush,ArpRequest,ArpCancel };Handle安装ArpProto的Handle。 List对应到ARP_SERVICE_DATA中的ChildrenList两者构成链表。 ConfigDataARP的配置参数在EFI_ARP_CONFIG_DATA中会进一步介绍。 Configured用来表示ARP是否已经配置。 InDestroy用于防止重入的标志。
EFI_ARP_CONFIG_DATA ARP也需要配置所以存在这个结构体其实现如下 typedef struct {////// 16-bit protocol type number in host byte order.///UINT16 SwAddressType;////// The length in bytes of the stations protocol address to register.///UINT8 SwAddressLength;////// The pointer to the first byte of the protocol address to register. For/// example, if SwAddressType is 0x0800 (IP), then/// StationAddress points to the first byte of this stations IP/// address stored in network byte order.///VOID *StationAddress;////// The timeout value in 100-ns units that is associated with each/// new dynamic ARP cache entry. If it is set to zero, the value is/// implementation-specific.///UINT32 EntryTimeOut;////// The number of retries before a MAC address is resolved. If it is/// set to zero, the value is implementation-specific.///UINT32 RetryCount;////// The timeout value in 100-ns units that is used to wait for the ARP/// reply packet or the timeout value between two retries. Set to zero/// to use implementation-specific value.///UINT32 RetryTimeOut; } EFI_ARP_CONFIG_DATA;SwAddressType对应以太网帧的类型在MNP章节中已经介绍过。SwAddressLength、StationAddress表示IP地址和长度。EntryTimeOutAPR缓存的过期时间。RetryCount、RetryTimeOutARP重试次数和超时时间。 ARP_CACHE_ENTRY 该结构体用来存放每一个ARP缓存项其中的重点就是IP和MAC的对应。其结构体如下 typedef union {UINT8 ProtoAddress[ARP_MAX_PROTOCOL_ADDRESS_LEN];UINT8 HwAddress[ARP_MAX_HARDWARE_ADDRESS_LEN]; } NET_ARP_ADDRESS_UNION;// // ARP address structure in an ARP packet. // typedef struct {UINT16 Type;UINT8 Length;UINT8 *AddressPtr;NET_ARP_ADDRESS_UNION Buffer; } NET_ARP_ADDRESS;// // Enumeration for ARP address type. // typedef enum {Hardware,Protocol } ARP_ADDRESS_TYPE;// // ARP cache entry definition. // typedef struct {LIST_ENTRY List;UINT32 RetryCount;UINT32 DefaultDecayTime;UINT32 DecayTime;UINT32 NextRetryTime;NET_ARP_ADDRESS Addresses[2];LIST_ENTRY UserRequestList; } ARP_CACHE_ENTRY;虽然有几层结构体的包装但是可以看到最重要的还是Addresses它的两个成员分别是 // // Enumeration for ARP address type. // typedef enum {Hardware,Protocol } ARP_ADDRESS_TYPE;一个表示IP另一个表示MAC地址。 ArpSendFrame ArpSendFrame()可以说是ARP驱动中最重要的函数其实现如下 VOID ArpSendFrame (IN ARP_INSTANCE_DATA *Instance,IN ARP_CACHE_ENTRY *CacheEntry,IN UINT16 ArpOpCode) {//// Allocate memory for the TxToken.//TxToken AllocatePool (sizeof (EFI_MANAGED_NETWORK_COMPLETION_TOKEN));TxToken-Event NULL;TxData NULL;Packet NULL;//// Create the event for this TxToken.//Status gBS-CreateEvent (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,ArpOnFrameSent,(VOID *)TxToken,TxToken-Event);//// Allocate memory for the TxData used in the TxToken.//TxData AllocatePool (sizeof (EFI_MANAGED_NETWORK_TRANSMIT_DATA));ArpService Instance-ArpService;SnpMode ArpService-SnpMode;ConfigData Instance-ConfigData;//// Calculate the buffer length for this arp frame.//TotalLength SnpMode-MediaHeaderSize sizeof (ARP_HEAD) 2 * (ConfigData-SwAddressLength SnpMode-HwAddressSize);//// Allocate buffer for the arp frame.//// 这里开始构建ARP包其具体的内容前面已经介绍过TmpPtr Packet;//// The destination MAC address.//// 根据是接受还是发送数据包ARP包的内容会不同if (ArpOpCode ARP_OPCODE_REQUEST) {CopyMem (TmpPtr, SnpMode-BroadcastAddress, SnpMode-HwAddressSize);} else {CopyMem (TmpPtr,CacheEntry-Addresses[Hardware].AddressPtr,SnpMode-HwAddressSize);}TmpPtr SnpMode-HwAddressSize;//// The source MAC address.//CopyMem (TmpPtr, SnpMode-CurrentAddress, SnpMode-HwAddressSize);TmpPtr SnpMode-HwAddressSize;//// The ethernet protocol type.//*(UINT16 *)TmpPtr HTONS (ARP_ETHER_PROTO_TYPE);TmpPtr 2;//// The ARP Head.//ArpHead (ARP_HEAD *)TmpPtr;ArpHead-HwType HTONS ((UINT16)SnpMode-IfType);ArpHead-ProtoType HTONS (ConfigData-SwAddressType);ArpHead-HwAddrLen (UINT8)SnpMode-HwAddressSize;ArpHead-ProtoAddrLen ConfigData-SwAddressLength;ArpHead-OpCode HTONS (ArpOpCode);TmpPtr sizeof (ARP_HEAD);//// The sender hardware address.//CopyMem (TmpPtr, SnpMode-CurrentAddress, SnpMode-HwAddressSize);TmpPtr SnpMode-HwAddressSize;//// The sender protocol address.//CopyMem (TmpPtr, ConfigData-StationAddress, ConfigData-SwAddressLength);TmpPtr ConfigData-SwAddressLength;//// The target hardware address.//CopyMem (TmpPtr,CacheEntry-Addresses[Hardware].AddressPtr,SnpMode-HwAddressSize);TmpPtr SnpMode-HwAddressSize;//// The target protocol address.//CopyMem (TmpPtr,CacheEntry-Addresses[Protocol].AddressPtr,ConfigData-SwAddressLength);//// Set all the fields of the TxData.//TxData-DestinationAddress NULL;TxData-SourceAddress NULL;TxData-ProtocolType 0;TxData-DataLength TotalLength - SnpMode-MediaHeaderSize;TxData-HeaderLength (UINT16)SnpMode-MediaHeaderSize;TxData-FragmentCount 1;// 真正的数据在这里TxData-FragmentTable[0].FragmentBuffer Packet;TxData-FragmentTable[0].FragmentLength TotalLength;//// Associate the TxData with the TxToken.//TxToken-Packet.TxData TxData;TxToken-Status EFI_NOT_READY;//// Send out this arp packet by Mnp.//Status ArpService-Mnp-Transmit (ArpService-Mnp, TxToken); }ArpSendFrame()会根据ARP包是接受后的反馈还是直接的发送进行区分产生不同的ARP包最终通过MNP的接口发送出去。 ARP事件 ArpOnFrameRcvd ArpOnFrameRcvd是Token中的回调函数其创建代码如下 //// Create the event used in the RxToken.//Status gBS-CreateEvent (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,ArpOnFrameRcvd,ArpService,ArpService-RxToken.Event);注意这个不是定时事件而是由其它的代码触发的主要就是MNP也就是说它是MNP接收到数据之后会执行的回调所以它就可以用来接收ARP包并进行解析和处理它的第一层实现很简单 VOID EFIAPI ArpOnFrameRcvd (IN EFI_EVENT Event,IN VOID *Context) {//// Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK//QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context); }这里可以看到对DPC的使用。ArpOnFrameRcvdDpc()的实现主要包含以下的内容 一系列的基础内容判断。判断是否在DeniedCacheTable中如果是就不处理这个包。判断IP是否一致如果是一致的表示正是本ARP需要处理的才会有后面的操作。判断是否在ResolvedCacheTable中表示已经处理过了的如果不是则增加。 由于MNP一直在接收数据再加上这个ARP事件的处理所以BIOS可以处理外部网络的ARP请求。 ArpTimerHandler 用来处理ARP_SERVICE_DATA中的PendingRequestTable、DeniedCacheTable和ResolvedCacheTable。第一个和后面两个的处理方式是不同的第一个的重点是重发ARP包 ArpSendFrame (RequestContext-Instance, CacheEntry, ARP_OPCODE_REQUEST);后面两个的重点是链表操作 RemoveEntryList (CacheEntry-List);注意这个定时事件在ARP服务创建之后就启动了 //// Start the heartbeat timer.//Status gBS-SetTimer (ArpService-PeriodicTimer,TimerPeriodic,ARP_PERIODIC_TIMER_INTERVAL // 500毫秒);它完成重试、缓存清理等操作。 ARP的使用 ARP的使用包括两个部分第一部分是响应其它网络的ARP包这个部分主要就是ArpOnFrameRcvd的实现第二部分就是自己发送ARP包来获取指定IP对应的MAC地址。关于第二部分主要在IP4和PXE等模块中以前者为例主要在函数Ip4SendFrame()中 //// First check whether this binding is in the ARP cache.//NextHop HTONL (NextHop);Status Arp-Request (Arp, NextHop, NULL, Token-DstMac);// 中间略//// First frame to NextHop, issue an asynchronous ARP requests//ArpQue Ip4CreateArpQue (Interface, NextHop);Status Arp-Request (Arp, ArpQue-Ip, ArpQue-OnResolved, ArpQue-Mac.Addr);InsertHeadList (ArpQue-Frames, Token-Link);InsertHeadList (Interface-ArpQues, ArpQue-Link);return EFI_SUCCESS;这里主要调用的是EFI_ARP_PROTOCOL中的Request成员函数后面会进一步介绍。 EFI_ARP_PROTOCOL 该Protocol的结构体如下 /// /// ARP is used to resolve local network protocol addresses into /// network hardware addresses. /// struct _EFI_ARP_PROTOCOL {EFI_ARP_CONFIGURE Configure;EFI_ARP_ADD Add;EFI_ARP_FIND Find;EFI_ARP_DELETE Delete;EFI_ARP_FLUSH Flush;EFI_ARP_REQUEST Request;EFI_ARP_CANCEL Cancel; };对应的实现在NetworkPkg\ArpDxe\ArpImpl.c EFI_ARP_PROTOCOL mEfiArpProtocolTemplate {ArpConfigure,ArpAdd,ArpFind,ArpDelete,ArpFlush,ArpRequest,ArpCancel };后面会介绍这些函数的实现。 Arp.Configure 对应的实现是ArpConfigure其代码实现 EFI_STATUS EFIAPI ArpConfigure (IN EFI_ARP_PROTOCOL *This,IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL) {//// Configure this instance, the ConfigData has already passed the basic checks.//Status ArpConfigureInstance (Instance, ConfigData); }最终的实现在ArpConfigureInstance() EFI_STATUS ArpConfigureInstance (IN ARP_INSTANCE_DATA *Instance,IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL) {if (ConfigData ! NULL) {// 如果已经配置过了那么就是更新配置if (Instance-Configured) {//// The instance is configured, check the unchangeable fields.//if ((OldConfigData-SwAddressType ! ConfigData-SwAddressType) ||(OldConfigData-SwAddressLength ! ConfigData-SwAddressLength) ||(CompareMem (OldConfigData-StationAddress,ConfigData-StationAddress,OldConfigData-SwAddressLength) ! 0)){//// Deny the unallowed changes.//return EFI_ACCESS_DENIED;}} else {//// The instance is not configured.//if (ConfigData-SwAddressType IPV4_ETHER_PROTO_TYPE) {CopyMem (Ip, ConfigData-StationAddress, sizeof (IP4_ADDR));if (IP4_IS_UNSPECIFIED (Ip) || IP4_IS_LOCAL_BROADCAST (Ip)) {//// The station address should not be zero or broadcast address.//return EFI_INVALID_PARAMETER;}}//// Save the configuration.//CopyMem (OldConfigData, ConfigData, sizeof (*OldConfigData));OldConfigData-StationAddress AllocatePool (OldConfigData-SwAddressLength);if (OldConfigData-StationAddress NULL) {return EFI_OUT_OF_RESOURCES;}//// Save the StationAddress.//CopyMem (OldConfigData-StationAddress,ConfigData-StationAddress,OldConfigData-SwAddressLength);//// Set the state to configured.//Instance-Configured TRUE;}//// Use the implementation specific values if the following field is zero.//OldConfigData-EntryTimeOut (ConfigData-EntryTimeOut 0) ?ARP_DEFAULT_TIMEOUT_VALUE : ConfigData-EntryTimeOut;OldConfigData-RetryCount (ConfigData-RetryCount 0) ?ARP_DEFAULT_RETRY_COUNT : ConfigData-RetryCount;OldConfigData-RetryTimeOut (ConfigData-RetryTimeOut 0) ?ARP_DEFAULT_RETRY_INTERVAL : ConfigData-RetryTimeOut;} else {//// Reset the configuration.//if (Instance-Configured) {//// Cancel the arp requests issued by this instance.//Instance-ArpProto.Cancel (Instance-ArpProto, NULL, NULL);//// Free the buffer previously allocated to hold the station address.//FreePool (OldConfigData-StationAddress);}Instance-Configured FALSE;} }如果入参ConfigData的值是NULL则表示重置配置否则就会根据入参进行配置。 Arp.Add 对应的实现是ArpAdd该函数最终会将IP和MAC地址写入到ARP_SERVICE_DATA的DeniedCacheTable或者ResolvedCacheTable表中。其它的成员函数比如FInd、Delete、Flush等也都是这些表的操作这里不再过多介绍。 Arp.Request 对应的实现是ArpRequest它会去获取指定IP的MAC地址 EFI_STATUS EFIAPI ArpRequest (IN EFI_ARP_PROTOCOL *This,IN VOID *TargetSwAddress OPTIONAL,IN EFI_EVENT ResolvedEvent OPTIONAL,OUT VOID TargetHwAddress) {if (!Instance-Configured) {return EFI_NOT_STARTED;}Status EFI_SUCCESS;ArpService Instance-ArpService;SnpMode ArpService-SnpMode;if ((TargetSwAddress NULL) ||((Instance-ConfigData.SwAddressType IPV4_ETHER_PROTO_TYPE) IP4_IS_LOCAL_BROADCAST (((UINT32 )TargetSwAddress)))){//// Return the hardware broadcast address.//CopyMem (TargetHwAddress, SnpMode-BroadcastAddress, SnpMode-HwAddressSize);goto SIGNAL_USER;}if ((Instance-ConfigData.SwAddressType IPV4_ETHER_PROTO_TYPE) IP4_IS_MULTICAST (NTOHL (((UINT32 *)TargetSwAddress)))){//// If the software address is an IPv4 multicast address, invoke Mnp to// resolve the address.//Status ArpService-Mnp-McastIpToMac (ArpService-Mnp,FALSE,TargetSwAddress,TargetHwAddress);goto SIGNAL_USER;}HardwareAddress.Type SnpMode-IfType;HardwareAddress.Length (UINT8)SnpMode-HwAddressSize;HardwareAddress.AddressPtr NULL;ProtocolAddress.Type Instance-ConfigData.SwAddressType;ProtocolAddress.Length Instance-ConfigData.SwAddressLength;ProtocolAddress.AddressPtr TargetSwAddress;//// Initialize the TargetHwAddress to a zero address.//ZeroMem (TargetHwAddress, SnpMode-HwAddressSize);OldTpl gBS-RaiseTPL (TPL_CALLBACK);//// Check whether the software address is in the denied table.//CacheEntry ArpFindDeniedCacheEntry (ArpService, ProtocolAddress, NULL);if (CacheEntry ! NULL) {Status EFI_ACCESS_DENIED;goto UNLOCK_EXIT;}//// Check whether the software address is already resolved.//CacheEntry ArpFindNextCacheEntryInTable (ArpService-ResolvedCacheTable,NULL,ByProtoAddress,ProtocolAddress,NULL);if (CacheEntry ! NULL) {//// Resolved, copy the address into the user buffer.//CopyMem (TargetHwAddress,CacheEntry-Addresses[Hardware].AddressPtr,CacheEntry-Addresses[Hardware].Length);goto UNLOCK_EXIT;}//// Create a request context for this arp request.//RequestContext AllocatePool (sizeof (USER_REQUEST_CONTEXT));RequestContext-Instance Instance;RequestContext-UserRequestEvent ResolvedEvent;RequestContext-UserHwAddrBuffer TargetHwAddress;InitializeListHead (RequestContext-List);//// Check whether there is a same request.//CacheEntry ArpFindNextCacheEntryInTable (ArpService-PendingRequestTable,NULL,ByProtoAddress,ProtocolAddress,NULL);if (CacheEntry ! NULL) {CacheEntry-NextRetryTime Instance-ConfigData.RetryTimeOut;CacheEntry-RetryCount Instance-ConfigData.RetryCount;} else {//// Allocate a cache entry for this request.//CacheEntry ArpAllocCacheEntry (Instance);if (CacheEntry NULL) {DEBUG ((DEBUG_ERROR, ArpRequest: Allocate memory for CacheEntry failed.\n));FreePool (RequestContext);Status EFI_OUT_OF_RESOURCES;goto UNLOCK_EXIT;}//// Fill the software address.//ArpFillAddressInCacheEntry (CacheEntry, HardwareAddress, ProtocolAddress);//// Add this entry into the PendingRequestTable.//InsertTailList (ArpService-PendingRequestTable, CacheEntry-List);}//// Link this request context into the cache entry.//InsertHeadList (CacheEntry-UserRequestList, RequestContext-List);//// Send out the ARP Request frame.//ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REQUEST);Status EFI_NOT_READY;UNLOCK_EXIT:gBS-RestoreTPL (OldTpl);SIGNAL_USER:if ((ResolvedEvent ! NULL) (Status EFI_SUCCESS)) {gBS-SignalEvent (ResolvedEvent);//// Dispatch the DPC queued by the NotifyFunction of ResolvedEvent.//DispatchDpc ();} }这里用到了前面介绍的ArpSendFrame。注意这里并不会简单就返回结果理由还是跟之前说的一样CPU的执行速度会快于网卡从ARP代码示例可以看到真正有效的用法。此外使用该函数还可以获取广播和多播地址。 ARP代码示例 通过ARP接口获取指定IP的MAC地址是一种常用的做法下面是一个示例代码位于BeniPkg\DynamicCommand\TestDynamicCommand\TestArp.c VOID CheckArp (IN EFI_HANDLE Handle,IN CONST CHAR16 *SrcString,IN CONST CHAR16 *DstString) {EFI_STATUS Status EFI_ABORTED;EFI_ARP_PROTOCOL *Arp NULL;EFI_HANDLE ArpHandle NULL;EFI_IPv4_ADDRESS SrcIp;EFI_IPv4_ADDRESS DestIp;IP4_ADDR IpAddr;EFI_MAC_ADDRESS Mac;EFI_MAC_ADDRESS ZeroMac;EFI_ARP_CONFIG_DATA ArpConfig;EFI_EVENT ResolvedEvent;BOOLEAN IsResolved FALSE;ZeroMem (Mac, sizeof (EFI_MAC_ADDRESS));ZeroMem (ZeroMac, sizeof (EFI_MAC_ADDRESS));Print (LResolving IP %s …\r\n, DstString);Status NetLibCreateServiceChild (Handle,Handle,gEfiArpServiceBindingProtocolGuid,ArpHandle);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;}Status gBS-OpenProtocol (ArpHandle,gEfiArpProtocolGuid,(VOID **)(Arp),Handle,Handle,EFI_OPEN_PROTOCOL_BY_DRIVER);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;}Status NetLibStrToIp4 (SrcString, SrcIp);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;} else {Print (LSource IP : %d.%d.%d.%d\r\n,SrcIp.Addr[0],SrcIp.Addr[1],SrcIp.Addr[2],SrcIp.Addr[3]);}Status NetLibStrToIp4 (DstString, DestIp);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;} else {Print (LDestination IP: %d.%d.%d.%d\r\n,DestIp.Addr[0],DestIp.Addr[1],DestIp.Addr[2],DestIp.Addr[3]);}IpAddr EFI_NTOHL (SrcIp);IpAddr HTONL (IpAddr);ArpConfig.SwAddressType 0x0800;ArpConfig.SwAddressLength 4;ArpConfig.StationAddress IpAddr;ArpConfig.EntryTimeOut 0;ArpConfig.RetryCount 0;ArpConfig.RetryTimeOut 0;Status Arp-Configure (Arp, NULL);Status Arp-Configure (Arp, ArpConfig);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;}Status gBS-CreateEvent (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,CheckIfResolved,IsResolved,ResolvedEvent);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;}Status Arp-Request (Arp, DestIp, ResolvedEvent, Mac);if (EFI_ERROR (Status) (Status ! EFI_NOT_READY)) {DEBUG ((EFI_D_ERROR, [%a][%d] Failed. - %r\n, FUNCTION, LINE, Status));return;}while (!IsResolved) {if (CompareMem (Mac, ZeroMac, sizeof (EFI_MAC_ADDRESS)) ! 0) {break;}}Print (LMAC: %02x:%02x:%02x:%02x:%02x:%02x\r\n,Mac.Addr[0],Mac.Addr[1],Mac.Addr[2],Mac.Addr[3],Mac.Addr[4],Mac.Addr[5]); }代码中的重点主要是两个一个是ARP的配置另一个是ARP的请求执行结果如下