xref: /reactos/drivers/network/tcpip/ip/network/arp.c (revision 1734f297)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS TCP/IP protocol driver
4  * FILE:        datalink/arp.c
5  * PURPOSE:     Address Resolution Protocol routines
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 01/08-2000 Created
9  */
10 
11 #include "precomp.h"
12 
PrepareARPPacket(PIP_INTERFACE IF,USHORT HardwareType,USHORT ProtocolType,UCHAR LinkAddressLength,UCHAR ProtoAddressLength,PVOID SenderLinkAddress,PVOID SenderProtoAddress,PVOID TargetLinkAddress,PVOID TargetProtoAddress,USHORT Opcode)13 PNDIS_PACKET PrepareARPPacket(
14     PIP_INTERFACE IF,
15     USHORT HardwareType,
16     USHORT ProtocolType,
17     UCHAR LinkAddressLength,
18     UCHAR ProtoAddressLength,
19     PVOID SenderLinkAddress,
20     PVOID SenderProtoAddress,
21     PVOID TargetLinkAddress,
22     PVOID TargetProtoAddress,
23     USHORT Opcode)
24 /*
25  * FUNCTION: Prepares an ARP packet
26  * ARGUMENTS:
27  *     HardwareType       = Hardware type (in network byte order)
28  *     ProtocolType       = Protocol type (in network byte order)
29  *     LinkAddressLength  = Length of link address fields
30  *     ProtoAddressLength = Length of protocol address fields
31  *     SenderLinkAddress  = Sender's link address
32  *     SenderProtoAddress = Sender's protocol address
33  *     TargetLinkAddress  = Target's link address (NULL if don't care)
34  *     TargetProtoAddress = Target's protocol address
35  *     Opcode             = ARP opcode (in network byte order)
36  * RETURNS:
37  *     Pointer to NDIS packet, NULL if there is not enough free resources
38  */
39 {
40     PNDIS_PACKET NdisPacket;
41     NDIS_STATUS NdisStatus;
42     PARP_HEADER Header;
43     PVOID DataBuffer;
44     ULONG Size, Contig;
45 
46     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
47 
48     /* Prepare ARP packet */
49     Size = sizeof(ARP_HEADER) +
50         2 * LinkAddressLength + /* Hardware address length */
51         2 * ProtoAddressLength; /* Protocol address length */
52     Size = MAX(Size, IF->MinFrameSize - IF->HeaderSize);
53 
54     NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, Size );
55     if( !NT_SUCCESS(NdisStatus) ) return NULL;
56 
57     GetDataPtr( NdisPacket, 0, (PCHAR *)&DataBuffer, (PUINT)&Contig );
58     ASSERT(DataBuffer);
59 
60     RtlZeroMemory(DataBuffer, Size);
61     Header = (PARP_HEADER)((ULONG_PTR)DataBuffer);
62     Header->HWType       = HardwareType;
63     Header->ProtoType    = ProtocolType;
64     Header->HWAddrLen    = LinkAddressLength;
65     Header->ProtoAddrLen = ProtoAddressLength;
66     Header->Opcode       = Opcode; /* Already swapped */
67     DataBuffer = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
68 
69     /* Our hardware address */
70     RtlCopyMemory(DataBuffer, SenderLinkAddress, LinkAddressLength);
71     DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
72 
73     /* Our protocol address */
74     RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength);
75 
76     if (TargetLinkAddress) {
77         DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength);
78         /* Target hardware address */
79         RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength);
80         DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
81     } else
82         /* Don't care about target hardware address */
83         DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength + LinkAddressLength);
84 
85     /* Target protocol address */
86     RtlCopyMemory(DataBuffer, TargetProtoAddress, ProtoAddressLength);
87 
88     return NdisPacket;
89 }
90 
91 
ARPTransmitComplete(PVOID Context,PNDIS_PACKET NdisPacket,NDIS_STATUS NdisStatus)92 VOID ARPTransmitComplete(
93     PVOID Context,
94     PNDIS_PACKET NdisPacket,
95     NDIS_STATUS NdisStatus)
96 /*
97  * FUNCTION: ARP request transmit completion handler
98  * ARGUMENTS:
99  *     Context    = Pointer to context information (IP_INTERFACE)
100  *     Packet     = Pointer to NDIS packet that was sent
101  *     NdisStatus = NDIS status of operation
102  * NOTES:
103  *    This routine is called when an ARP request has been sent
104  */
105 {
106     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
107     FreeNdisPacket(NdisPacket);
108 }
109 
110 
ARPTransmit(PIP_ADDRESS Address,PVOID LinkAddress,PIP_INTERFACE Interface)111 BOOLEAN ARPTransmit(PIP_ADDRESS Address, PVOID LinkAddress,
112                     PIP_INTERFACE Interface)
113 /*
114  * FUNCTION: Creates an ARP request and transmits it on a network
115  * ARGUMENTS:
116  *     Address = Pointer to IP address to resolve
117  * RETURNS:
118  *     TRUE if the request was successfully sent, FALSE if not
119  */
120 {
121     PNDIS_PACKET NdisPacket;
122     UCHAR ProtoAddrLen;
123     USHORT ProtoType;
124 
125     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
126 
127     /* If Address is NULL then the caller wants an
128      * gratuitous ARP packet sent */
129     if (!Address)
130         Address = &Interface->Unicast;
131 
132     switch (Address->Type) {
133         case IP_ADDRESS_V4:
134             ProtoType    = (USHORT)ETYPE_IPv4; /* IPv4 */
135             ProtoAddrLen = 4;                  /* Length of IPv4 address */
136             break;
137         case IP_ADDRESS_V6:
138             ProtoType    = (USHORT)ETYPE_IPv6; /* IPv6 */
139             ProtoAddrLen = 16;                 /* Length of IPv6 address */
140             break;
141         default:
142 	    TI_DbgPrint(DEBUG_ARP,("Bad Address Type %x\n", Address->Type));
143 	    DbgBreakPoint();
144             /* Should not happen */
145             return FALSE;
146     }
147 
148     NdisPacket = PrepareARPPacket(
149         Interface,
150         WN2H(0x0001),                    /* FIXME: Ethernet only */
151         ProtoType,                       /* Protocol type */
152         (UCHAR)Interface->AddressLength, /* Hardware address length */
153         (UCHAR)ProtoAddrLen,             /* Protocol address length */
154         Interface->Address,              /* Sender's (local) hardware address */
155         &Interface->Unicast.Address.IPv4Address,/* Sender's (local) protocol address */
156         LinkAddress,                     /* Target's (remote) hardware address */
157         &Address->Address.IPv4Address,   /* Target's (remote) protocol address */
158         ARP_OPCODE_REQUEST);             /* ARP request */
159 
160     if( !NdisPacket ) return FALSE;
161 
162     ASSERT_KM_POINTER(NdisPacket);
163     ASSERT_KM_POINTER(PC(NdisPacket));
164     PC(NdisPacket)->DLComplete = ARPTransmitComplete;
165 
166     TI_DbgPrint(DEBUG_ARP,("Sending ARP Packet\n"));
167 
168     (*Interface->Transmit)(Interface->Context, NdisPacket,
169         0, NULL, LAN_PROTO_ARP);
170 
171     return TRUE;
172 }
173 
174 
ARPReceive(PVOID Context,PIP_PACKET Packet)175 VOID ARPReceive(
176     PVOID Context,
177     PIP_PACKET Packet)
178 /*
179  * FUNCTION: Receives an ARP packet
180  * ARGUMENTS:
181  *     Context = Pointer to context information (IP_INTERFACE)
182  *     Packet  = Pointer to packet
183  */
184 {
185     PARP_HEADER Header;
186     IP_ADDRESS SrcAddress;
187     IP_ADDRESS DstAddress;
188     PCHAR SenderHWAddress, SenderProtoAddress, TargetProtoAddress;
189     PNEIGHBOR_CACHE_ENTRY NCE;
190     PNDIS_PACKET NdisPacket;
191     PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
192     ULONG BytesCopied, DataSize;
193     PCHAR DataBuffer;
194 
195     PAGED_CODE();
196 
197     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
198 
199     Packet->Header = ExAllocatePoolWithTag(PagedPool,
200                                            sizeof(ARP_HEADER),
201                                            PACKET_BUFFER_TAG);
202     if (!Packet->Header)
203     {
204         TI_DbgPrint(DEBUG_ARP, ("Unable to allocate header buffer\n"));
205         Packet->Free(Packet);
206         return;
207     }
208     Packet->MappedHeader = FALSE;
209 
210     BytesCopied = CopyPacketToBuffer((PCHAR)Packet->Header,
211                                      Packet->NdisPacket,
212                                      Packet->Position,
213                                      sizeof(ARP_HEADER));
214     if (BytesCopied != sizeof(ARP_HEADER))
215     {
216         TI_DbgPrint(DEBUG_ARP, ("Unable to copy in header buffer\n"));
217         Packet->Free(Packet);
218         return;
219     }
220 
221     Header = (PARP_HEADER)Packet->Header;
222 
223     /* FIXME: Ethernet only */
224     if (WN2H(Header->HWType) != 1) {
225         TI_DbgPrint(DEBUG_ARP, ("Unknown ARP hardware type (0x%X).\n", WN2H(Header->HWType)));
226         Packet->Free(Packet);
227         return;
228     }
229 
230     /* Check protocol type */
231     if (Header->ProtoType != ETYPE_IPv4) {
232         TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
233         Packet->Free(Packet);
234         return;
235     }
236 
237     DataSize = (2 * Header->HWAddrLen) + (2 * Header->ProtoAddrLen);
238     DataBuffer = ExAllocatePool(PagedPool,
239                                 DataSize);
240     if (!DataBuffer)
241     {
242         TI_DbgPrint(DEBUG_ARP, ("Unable to allocate data buffer\n"));
243         Packet->Free(Packet);
244         return;
245     }
246 
247     BytesCopied = CopyPacketToBuffer(DataBuffer,
248                                      Packet->NdisPacket,
249                                      Packet->Position + sizeof(ARP_HEADER),
250                                      DataSize);
251     if (BytesCopied != DataSize)
252     {
253         TI_DbgPrint(DEBUG_ARP, ("Unable to copy in data buffer\n"));
254         ExFreePool(DataBuffer);
255         Packet->Free(Packet);
256         return;
257     }
258 
259     SenderHWAddress    = (PVOID)(DataBuffer);
260     SenderProtoAddress = (PVOID)(SenderHWAddress + Header->HWAddrLen);
261     TargetProtoAddress = (PVOID)(SenderProtoAddress + Header->ProtoAddrLen + Header->HWAddrLen);
262 
263     AddrInitIPv4(&DstAddress, *((PULONG)TargetProtoAddress));
264     if (!AddrIsEqual(&DstAddress, &Interface->Unicast))
265     {
266         ExFreePool(DataBuffer);
267         Packet->Free(Packet);
268         return;
269     }
270 
271     AddrInitIPv4(&SrcAddress, *((PULONG)SenderProtoAddress));
272 
273     /* Check if we know the sender */
274     NCE = NBLocateNeighbor(&SrcAddress, Interface);
275     if (NCE) {
276         /* We know the sender. Update the hardware address
277            and state in our neighbor address cache */
278         NBUpdateNeighbor(NCE, SenderHWAddress, 0);
279     } else {
280         /* The packet had our protocol address as target. The sender
281            may want to communicate with us soon, so add his address
282            to our address cache */
283         NBAddNeighbor(Interface, &SrcAddress, SenderHWAddress,
284                       Header->HWAddrLen, 0, ARP_COMPLETE_TIMEOUT);
285     }
286 
287     if (Header->Opcode != ARP_OPCODE_REQUEST)
288     {
289         ExFreePool(DataBuffer);
290         Packet->Free(Packet);
291         return;
292     }
293 
294     /* This is a request for our address. Swap the addresses and
295        send an ARP reply back to the sender */
296     NdisPacket = PrepareARPPacket(
297         Interface,
298         Header->HWType,                  /* Hardware type */
299         Header->ProtoType,               /* Protocol type */
300         (UCHAR)Interface->AddressLength, /* Hardware address length */
301         (UCHAR)Header->ProtoAddrLen,     /* Protocol address length */
302         Interface->Address,              /* Sender's (local) hardware address */
303         &Interface->Unicast.Address.IPv4Address,/* Sender's (local) protocol address */
304         SenderHWAddress,                 /* Target's (remote) hardware address */
305         SenderProtoAddress,              /* Target's (remote) protocol address */
306         ARP_OPCODE_REPLY);               /* ARP reply */
307     if (NdisPacket) {
308         PC(NdisPacket)->DLComplete = ARPTransmitComplete;
309         (*Interface->Transmit)(Interface->Context,
310                                NdisPacket,
311                                0,
312                                SenderHWAddress,
313                                LAN_PROTO_ARP);
314     }
315 
316     ExFreePool(DataBuffer);
317     Packet->Free(Packet);
318 }
319 
320 /* EOF */
321