reactos/reactos/drivers/net/tcpip/datalink/arp.c
Casper Hornstrup 72c2af4ca7 Better RawIP support.
Minor fixes.

svn path=/trunk/; revision=1848
2001-05-01 22:34:01 +00:00

284 lines
9.3 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 <tcpip.h>
#include <arp.h>
#include <routines.h>
#include <neighbor.h>
#include <address.h>
#include <pool.h>
#include <lan.h>
PNDIS_PACKET PrepareARPPacket(
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;
PNDIS_BUFFER NdisBuffer;
NDIS_STATUS NdisStatus;
PARP_HEADER Header;
PVOID DataBuffer;
ULONG Size;
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
/* Prepare ARP packet */
Size = MaxLLHeaderSize + sizeof(ARP_HEADER) +
2 * LinkAddressLength + /* Hardware address length */
2 * ProtoAddressLength; /* Protocol address length */
Size = MAX(Size, MinLLFrameSize);
DataBuffer = ExAllocatePool(NonPagedPool, Size);
if (!DataBuffer)
return NULL;
/* Allocate NDIS packet */
NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
ExFreePool(DataBuffer);
return NULL;
}
/* Allocate NDIS buffer for maximum link level header and ARP packet */
NdisAllocateBuffer(&NdisStatus, &NdisBuffer, GlobalBufferPool,
DataBuffer, Size);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
NdisFreePacket(NdisPacket);
ExFreePool(DataBuffer);
return NULL;
}
/* Link NDIS buffer into packet */
NdisChainBufferAtFront(NdisPacket, NdisBuffer);
RtlZeroMemory(DataBuffer, Size);
Header = (PARP_HEADER)((ULONG_PTR)DataBuffer + MaxLLHeaderSize);
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);
(ULONG_PTR)DataBuffer += LinkAddressLength;
/* Our protocol address */
RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength);
if (TargetLinkAddress) {
(ULONG_PTR)DataBuffer += ProtoAddressLength;
/* Target hardware address */
RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength);
(ULONG_PTR)DataBuffer += LinkAddressLength;
} else
/* Don't care about target hardware address */
(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,
PNET_TABLE_ENTRY NTE)
/*
* FUNCTION: Creates an ARP request and transmits it on a network
* ARGUMENTS:
* Address = Pointer to IP address to resolve
* NTE = Pointer to net table entru to use for transmitting request
* RETURNS:
* TRUE if the request was successfully sent, FALSE if not
*/
{
PIP_INTERFACE Interface;
PNDIS_PACKET NdisPacket;
UCHAR ProtoAddrLen;
USHORT ProtoType;
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
Interface = NTE->Interface;
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:
/* Should not happen */
return FALSE;
}
NdisPacket = PrepareARPPacket(
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 */
&NTE->Address->Address, /* Sender's (local) protocol address */
NULL, /* Don't care */
&Address->Address, /* Target's (remote) protocol address */
ARP_OPCODE_REQUEST); /* ARP request */
PC(NdisPacket)->DLComplete = ARPTransmitComplete;
(*Interface->Transmit)(Interface->Context, NdisPacket,
MaxLLHeaderSize, 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;
PIP_ADDRESS Address;
PVOID SenderHWAddress;
PVOID SenderProtoAddress;
PVOID TargetProtoAddress;
PADDRESS_ENTRY ADE;
PNEIGHBOR_CACHE_ENTRY NCE;
PNDIS_PACKET NdisPacket;
PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
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)));
return;
}
/* Check protocol type */
if (Header->ProtoType != ETYPE_IPv4) {
TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
return;
}
SenderHWAddress = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
SenderProtoAddress = (PVOID)((ULONG_PTR)SenderHWAddress + Header->HWAddrLen);
/* Check if we have the target protocol address */
TargetProtoAddress = (PVOID)((ULONG_PTR)SenderProtoAddress +
Header->ProtoAddrLen + Header->HWAddrLen);
Address = AddrBuildIPv4(*((PULONG)TargetProtoAddress));
ADE = IPLocateADE(Address, ADE_UNICAST);
if (!ADE) {
TI_DbgPrint(DEBUG_ARP, ("Target address (0x%X) is not mine.\n", *((PULONG)TargetProtoAddress)));
return;
}
/* Check if we know the sender */
AddrInitIPv4(Address, *((PULONG)SenderProtoAddress));
NCE = NBLocateNeighbor(Address);
if (NCE) {
DereferenceObject(Address);
/* We know the sender. Update the hardware address
and state in our neighbor address cache */
NBUpdateNeighbor(NCE, SenderHWAddress, NUD_REACHABLE);
} 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 */
NCE = NBAddNeighbor(Interface, Address, SenderHWAddress,
Header->HWAddrLen, NUD_REACHABLE);
}
if (NCE)
DereferenceObject(NCE)
if (Header->Opcode != ARP_OPCODE_REQUEST)
return;
/* This is a request for our address. Swap the addresses and
send an ARP reply back to the sender */
NdisPacket = PrepareARPPacket(
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 */
&ADE->Address->Address, /* 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,
MaxLLHeaderSize,
SenderHWAddress,
LAN_PROTO_ARP);
}
}
/* EOF */