[SDK] One step further towards ReactOS source code tree restructure: the sdk folder...
[reactos.git] / reactos / sdk / lib / drivers / ip / network / arp.c
diff --git a/reactos/sdk/lib/drivers/ip/network/arp.c b/reactos/sdk/lib/drivers/ip/network/arp.c
new file mode 100644 (file)
index 0000000..9cd272b
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * 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 */