reactos/sdk/lib/drivers/ip/network/arp.c
2021-06-11 15:33:08 +03:00

321 lines
11 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS TCP/IP protocol driver
* FILE: datalink/arp.c
* PURPOSE: Address Resolution Protocol routines
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/08-2000 Created
*/
#include "precomp.h"
PNDIS_PACKET PrepareARPPacket(
PIP_INTERFACE IF,
USHORT HardwareType,
USHORT ProtocolType,
UCHAR LinkAddressLength,
UCHAR ProtoAddressLength,
PVOID SenderLinkAddress,
PVOID SenderProtoAddress,
PVOID TargetLinkAddress,
PVOID TargetProtoAddress,
USHORT Opcode)
/*
* FUNCTION: Prepares an ARP packet
* ARGUMENTS:
* HardwareType = Hardware type (in network byte order)
* ProtocolType = Protocol type (in network byte order)
* LinkAddressLength = Length of link address fields
* ProtoAddressLength = Length of protocol address fields
* SenderLinkAddress = Sender's link address
* SenderProtoAddress = Sender's protocol address
* TargetLinkAddress = Target's link address (NULL if don't care)
* TargetProtoAddress = Target's protocol address
* Opcode = ARP opcode (in network byte order)
* RETURNS:
* Pointer to NDIS packet, NULL if there is not enough free resources
*/
{
PNDIS_PACKET NdisPacket;
NDIS_STATUS NdisStatus;
PARP_HEADER Header;
PVOID DataBuffer;
ULONG Size, Contig;
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
/* Prepare ARP packet */
Size = sizeof(ARP_HEADER) +
2 * LinkAddressLength + /* Hardware address length */
2 * ProtoAddressLength; /* Protocol address length */
Size = MAX(Size, IF->MinFrameSize - IF->HeaderSize);
NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, Size );
if( !NT_SUCCESS(NdisStatus) ) return NULL;
GetDataPtr( NdisPacket, 0, (PCHAR *)&DataBuffer, (PUINT)&Contig );
ASSERT(DataBuffer);
RtlZeroMemory(DataBuffer, Size);
Header = (PARP_HEADER)((ULONG_PTR)DataBuffer);
Header->HWType = HardwareType;
Header->ProtoType = ProtocolType;
Header->HWAddrLen = LinkAddressLength;
Header->ProtoAddrLen = ProtoAddressLength;
Header->Opcode = Opcode; /* Already swapped */
DataBuffer = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
/* Our hardware address */
RtlCopyMemory(DataBuffer, SenderLinkAddress, LinkAddressLength);
DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
/* Our protocol address */
RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength);
if (TargetLinkAddress) {
DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength);
/* Target hardware address */
RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength);
DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
} else
/* Don't care about target hardware address */
DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength + LinkAddressLength);
/* Target protocol address */
RtlCopyMemory(DataBuffer, TargetProtoAddress, ProtoAddressLength);
return NdisPacket;
}
VOID ARPTransmitComplete(
PVOID Context,
PNDIS_PACKET NdisPacket,
NDIS_STATUS NdisStatus)
/*
* FUNCTION: ARP request transmit completion handler
* ARGUMENTS:
* Context = Pointer to context information (IP_INTERFACE)
* Packet = Pointer to NDIS packet that was sent
* NdisStatus = NDIS status of operation
* NOTES:
* This routine is called when an ARP request has been sent
*/
{
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
FreeNdisPacket(NdisPacket);
}
BOOLEAN ARPTransmit(PIP_ADDRESS Address, PVOID LinkAddress,
PIP_INTERFACE Interface)
/*
* FUNCTION: Creates an ARP request and transmits it on a network
* ARGUMENTS:
* Address = Pointer to IP address to resolve
* RETURNS:
* TRUE if the request was successfully sent, FALSE if not
*/
{
PNDIS_PACKET NdisPacket;
UCHAR ProtoAddrLen;
USHORT ProtoType;
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
/* If Address is NULL then the caller wants an
* gratuitous ARP packet sent */
if (!Address)
Address = &Interface->Unicast;
switch (Address->Type) {
case IP_ADDRESS_V4:
ProtoType = (USHORT)ETYPE_IPv4; /* IPv4 */
ProtoAddrLen = 4; /* Length of IPv4 address */
break;
case IP_ADDRESS_V6:
ProtoType = (USHORT)ETYPE_IPv6; /* IPv6 */
ProtoAddrLen = 16; /* Length of IPv6 address */
break;
default:
TI_DbgPrint(DEBUG_ARP,("Bad Address Type %x\n", Address->Type));
DbgBreakPoint();
/* Should not happen */
return FALSE;
}
NdisPacket = PrepareARPPacket(
Interface,
WN2H(0x0001), /* FIXME: Ethernet only */
ProtoType, /* Protocol type */
(UCHAR)Interface->AddressLength, /* Hardware address length */
(UCHAR)ProtoAddrLen, /* Protocol address length */
Interface->Address, /* Sender's (local) hardware address */
&Interface->Unicast.Address.IPv4Address,/* Sender's (local) protocol address */
LinkAddress, /* Target's (remote) hardware address */
&Address->Address.IPv4Address, /* Target's (remote) protocol address */
ARP_OPCODE_REQUEST); /* ARP request */
if( !NdisPacket ) return FALSE;
ASSERT_KM_POINTER(NdisPacket);
ASSERT_KM_POINTER(PC(NdisPacket));
PC(NdisPacket)->DLComplete = ARPTransmitComplete;
TI_DbgPrint(DEBUG_ARP,("Sending ARP Packet\n"));
(*Interface->Transmit)(Interface->Context, NdisPacket,
0, NULL, LAN_PROTO_ARP);
return TRUE;
}
VOID ARPReceive(
PVOID Context,
PIP_PACKET Packet)
/*
* FUNCTION: Receives an ARP packet
* ARGUMENTS:
* Context = Pointer to context information (IP_INTERFACE)
* Packet = Pointer to packet
*/
{
PARP_HEADER Header;
IP_ADDRESS SrcAddress;
IP_ADDRESS DstAddress;
PCHAR SenderHWAddress, SenderProtoAddress, TargetProtoAddress;
PNEIGHBOR_CACHE_ENTRY NCE;
PNDIS_PACKET NdisPacket;
PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
ULONG BytesCopied, DataSize;
PCHAR DataBuffer;
PAGED_CODE();
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
Packet->Header = ExAllocatePoolWithTag(PagedPool,
sizeof(ARP_HEADER),
PACKET_BUFFER_TAG);
if (!Packet->Header)
{
TI_DbgPrint(DEBUG_ARP, ("Unable to allocate header buffer\n"));
Packet->Free(Packet);
return;
}
Packet->MappedHeader = FALSE;
BytesCopied = CopyPacketToBuffer((PCHAR)Packet->Header,
Packet->NdisPacket,
Packet->Position,
sizeof(ARP_HEADER));
if (BytesCopied != sizeof(ARP_HEADER))
{
TI_DbgPrint(DEBUG_ARP, ("Unable to copy in header buffer\n"));
Packet->Free(Packet);
return;
}
Header = (PARP_HEADER)Packet->Header;
/* FIXME: Ethernet only */
if (WN2H(Header->HWType) != 1) {
TI_DbgPrint(DEBUG_ARP, ("Unknown ARP hardware type (0x%X).\n", WN2H(Header->HWType)));
Packet->Free(Packet);
return;
}
/* Check protocol type */
if (Header->ProtoType != ETYPE_IPv4) {
TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
Packet->Free(Packet);
return;
}
DataSize = (2 * Header->HWAddrLen) + (2 * Header->ProtoAddrLen);
DataBuffer = ExAllocatePool(PagedPool,
DataSize);
if (!DataBuffer)
{
TI_DbgPrint(DEBUG_ARP, ("Unable to allocate data buffer\n"));
Packet->Free(Packet);
return;
}
BytesCopied = CopyPacketToBuffer(DataBuffer,
Packet->NdisPacket,
Packet->Position + sizeof(ARP_HEADER),
DataSize);
if (BytesCopied != DataSize)
{
TI_DbgPrint(DEBUG_ARP, ("Unable to copy in data buffer\n"));
ExFreePool(DataBuffer);
Packet->Free(Packet);
return;
}
SenderHWAddress = (PVOID)(DataBuffer);
SenderProtoAddress = (PVOID)(SenderHWAddress + Header->HWAddrLen);
TargetProtoAddress = (PVOID)(SenderProtoAddress + Header->ProtoAddrLen + Header->HWAddrLen);
AddrInitIPv4(&DstAddress, *((PULONG)TargetProtoAddress));
if (!AddrIsEqual(&DstAddress, &Interface->Unicast))
{
ExFreePool(DataBuffer);
Packet->Free(Packet);
return;
}
AddrInitIPv4(&SrcAddress, *((PULONG)SenderProtoAddress));
/* Check if we know the sender */
NCE = NBLocateNeighbor(&SrcAddress, Interface);
if (NCE) {
/* We know the sender. Update the hardware address
and state in our neighbor address cache */
NBUpdateNeighbor(NCE, SenderHWAddress, 0);
} else {
/* The packet had our protocol address as target. The sender
may want to communicate with us soon, so add his address
to our address cache */
NBAddNeighbor(Interface, &SrcAddress, SenderHWAddress,
Header->HWAddrLen, 0, ARP_COMPLETE_TIMEOUT);
}
if (Header->Opcode != ARP_OPCODE_REQUEST)
{
ExFreePool(DataBuffer);
Packet->Free(Packet);
return;
}
/* This is a request for our address. Swap the addresses and
send an ARP reply back to the sender */
NdisPacket = PrepareARPPacket(
Interface,
Header->HWType, /* Hardware type */
Header->ProtoType, /* Protocol type */
(UCHAR)Interface->AddressLength, /* Hardware address length */
(UCHAR)Header->ProtoAddrLen, /* Protocol address length */
Interface->Address, /* Sender's (local) hardware address */
&Interface->Unicast.Address.IPv4Address,/* Sender's (local) protocol address */
SenderHWAddress, /* Target's (remote) hardware address */
SenderProtoAddress, /* Target's (remote) protocol address */
ARP_OPCODE_REPLY); /* ARP reply */
if (NdisPacket) {
PC(NdisPacket)->DLComplete = ARPTransmitComplete;
(*Interface->Transmit)(Interface->Context,
NdisPacket,
0,
SenderHWAddress,
LAN_PROTO_ARP);
}
ExFreePool(DataBuffer);
Packet->Free(Packet);
}
/* EOF */