mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
527f2f9057
* Create a branch for some evul shell experiments. svn path=/branches/shell-experiments/; revision=61927
320 lines
11 KiB
C
320 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 */
|