--- /dev/null
+/*
+ * 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 */