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

网站开发使用软件环境硬件环境网络广告推广公司

网站开发使用软件环境硬件环境,网络广告推广公司,土巴兔装修公司靠谱吗,企业培训系统appARP ARP协议说明 从这里开始涉及到的网络协议都是比较通用的了,在一般的TCP/IP四层模型中都能够看到这些内容,不过这里主要介绍的还是其在BIOS下的实现,但是在此之前还是需要先说明ARP的作用。 ARP的全称是Address Resolution Protocol&am…

ARP

ARP协议说明

从这里开始涉及到的网络协议都是比较通用的了,在一般的TCP/IP四层模型中都能够看到这些内容,不过这里主要介绍的还是其在BIOS下的实现,但是在此之前还是需要先说明ARP的作用。

ARP的全称是Address Resolution Protocol,它是一种解决地址问题的协议。以目标IP为线索,用来定义下一个应该接收数据分包的网络设备对应的MAC地址。ARP只用于IPv4,不能用于IPv6(IPv6使用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操作类型:
1:ARP请求
2:ARP应答
3:RARP请求
4:RARP应答
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网络协议栈中的关系图:

支持
提供
支持
支持
提供
支持
提供
提供
提供
支持
提供
提供
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函数的执行流程大致如下:

  1. 使用ArpCreateService()函数初始化ARP_SERVICE_DATA
  2. 安装gEfiArpServiceBindingProtocolGuid,对应的服务Protocol跟MNP的是一样的:
struct _EFI_SERVICE_BINDING_PROTOCOL {EFI_SERVICE_BINDING_CREATE_CHILD     CreateChild;EFI_SERVICE_BINDING_DESTROY_CHILD    DestroyChild;
};
  1. 通过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_DATAARP_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;
  • MnpChildHandleMnp:对应MNP中的EFI_MANAGED_NETWORK_PROTOCOL及其所在的Handle,这个Handle上面还有gEfiManagedNetworkServiceBindingProtocolGuid对应的服务Protocol。
  • MnpConfigData:MNP的配置参数,其值是固定的:
  //// 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模块的主要功能。

  • ChildrenListChildrenNumberArpServiceBindingCreateChild()创建的ARP子项的链表。

  • PendingRequestTable:处理ARP的重试。

  • DeniedCacheTableResolvedCacheTable:ARP缓存表,用来缓存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:创建子项的那个服务对应的数据。

  • ArpProto:ARP操作接口:

//
// 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,两者构成链表。

  • ConfigData:ARP的配置参数,在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 station's 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 station's 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章节中已经介绍过。
  • SwAddressLengthStationAddress:表示IP地址和长度。
  • EntryTimeOut:APR缓存的过期时间。
  • RetryCountRetryTimeOut:ARP重试次数和超时时间。

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()的实现主要包含以下的内容:

  1. 一系列的基础内容判断。
  2. 判断是否在DeniedCacheTable中,如果是就不处理这个包。
  3. 判断IP是否一致,如果是一致的,表示正是本ARP需要处理的,才会有后面的操作。
  4. 判断是否在ResolvedCacheTable中,表示已经处理过了的,如果不是,则增加。

由于MNP一直在接收数据,再加上这个ARP事件的处理,所以BIOS可以处理外部网络的ARP请求。

ArpTimerHandler

用来处理ARP_SERVICE_DATA中的PendingRequestTableDeniedCacheTableResolvedCacheTable。第一个和后面两个的处理方式是不同的,第一个的重点是重发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_DATADeniedCacheTable或者ResolvedCacheTable表中。其它的成员函数,比如FIndDeleteFlush等,也都是这些表的操作,这里不再过多介绍。

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 (L"Resolving 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 (L"Source 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 (L"Destination 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 (L"MAC: %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的请求,执行结果如下:

在这里插入图片描述

http://www.15wanjia.com/news/4657.html

相关文章:

  • 用手机怎样免费做网站重庆seowhy整站优化
  • 海洋高端的专业做网站seo资讯推推蛙
  • 深圳装修公司报价郑州百度seo排名公司
  • 电脑做ppt一般下载哪个网站好企业网站设计价格
  • 直播网站建设模板免费的短视频app大全下载
  • 相机网站建设策划书网络营销策划书论文
  • 全网最低价业务网站济南网络优化厂家
  • 网页美工设计第一步需要做什么seo搜索方法
  • 品牌建设 开鲁网站seo免费版
  • 任县网站制作独立站
  • dedecms模板 中医院网站全套模板营销工具
  • 公司网站建设规划网络推广怎么找客户
  • 深圳做网站做得比较好的公司市场营销的八个理论
  • 为什么需要响应式网站杭州网站优化服务
  • 美食网站联系我们怎么做企业网站seo贵不贵
  • 免费做网站教程关键词首页排名优化价格
  • 东莞商城网站开发郑州网站建设推广
  • 电商网站制作流程百度入驻商家
  • b站推广网站400今日重庆重要消息
  • 免费网站模板的制作方法广东企业网站seo哪里好
  • ssh做的大型网站线上招生引流推广方法
  • 赣州网站设计表白网站制作
  • 网站后面的官网是如何做的seo专家招聘
  • 企业网站 论文今天特大军事新闻
  • 外贸做网站的好处网络营销策划书ppt
  • 张槎建网站服务bt蚂蚁
  • 济南市网站建设百度快照优化的优势是什么
  • 重庆seo网站策划官方百度下载安装
  • 寻找大连网站建设推广页面制作
  • 开发平台官网杭州seo优化公司