/* * 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 #include #include #include #include #include #include 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 */