:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / net / tcpip / datalink / arp.c
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 #include <tcpip.h>
11 #include <arp.h>
12 #include <routines.h>
13 #include <neighbor.h>
14 #include <address.h>
15 #include <pool.h>
16 #include <lan.h>
17
18
19 PNDIS_PACKET PrepareARPPacket(
20     USHORT HardwareType,
21     USHORT ProtocolType,
22     UCHAR LinkAddressLength,
23     UCHAR ProtoAddressLength,
24     PVOID SenderLinkAddress,
25     PVOID SenderProtoAddress,
26     PVOID TargetLinkAddress,
27     PVOID TargetProtoAddress,
28     USHORT Opcode)
29 /*
30  * FUNCTION: Prepares an ARP packet
31  * ARGUMENTS:
32  *     HardwareType       = Hardware type (in network byte order)
33  *     ProtocolType       = Protocol type (in network byte order)
34  *     LinkAddressLength  = Length of link address fields
35  *     ProtoAddressLength = Length of protocol address fields
36  *     SenderLinkAddress  = Sender's link address
37  *     SenderProtoAddress = Sender's protocol address
38  *     TargetLinkAddress  = Target's link address (NULL if don't care)
39  *     TargetProtoAddress = Target's protocol address
40  *     Opcode             = ARP opcode (in network byte order)
41  * RETURNS:
42  *     Pointer to NDIS packet, NULL if there is not enough free resources
43  */
44 {
45     PNDIS_PACKET NdisPacket;
46     PNDIS_BUFFER NdisBuffer;
47     NDIS_STATUS NdisStatus;
48     PARP_HEADER Header;
49     PVOID DataBuffer;
50     ULONG Size;
51
52     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
53
54     /* Prepare ARP packet */
55     Size = MaxLLHeaderSize + sizeof(ARP_HEADER) + 
56         2 * LinkAddressLength + /* Hardware address length */
57         2 * ProtoAddressLength; /* Protocol address length */
58     Size = MAX(Size, MinLLFrameSize);
59
60     DataBuffer = ExAllocatePool(NonPagedPool, Size);
61     if (!DataBuffer)
62         return NULL;
63
64     /* Allocate NDIS packet */
65     NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);
66     if (NdisStatus != NDIS_STATUS_SUCCESS) {
67         ExFreePool(DataBuffer);
68         return NULL;
69     }
70
71     /* Allocate NDIS buffer for maximum link level header and ARP packet */
72     NdisAllocateBuffer(&NdisStatus, &NdisBuffer, GlobalBufferPool,
73         DataBuffer, Size);
74     if (NdisStatus != NDIS_STATUS_SUCCESS) {
75         NdisFreePacket(NdisPacket);
76         ExFreePool(DataBuffer);
77         return NULL;
78     }
79
80     /* Link NDIS buffer into packet */
81     NdisChainBufferAtFront(NdisPacket, NdisBuffer);
82     RtlZeroMemory(DataBuffer, Size);
83     Header = (PARP_HEADER)((ULONG_PTR)DataBuffer + MaxLLHeaderSize);
84     Header->HWType       = HardwareType;
85     Header->ProtoType    = ProtocolType;
86     Header->HWAddrLen    = LinkAddressLength;
87     Header->ProtoAddrLen = ProtoAddressLength;
88     Header->Opcode       = Opcode; /* Already swapped */
89     DataBuffer = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
90
91     /* Our hardware address */
92     RtlCopyMemory(DataBuffer, SenderLinkAddress, LinkAddressLength);
93     (ULONG_PTR)DataBuffer += LinkAddressLength;
94
95     /* Our protocol address */
96     RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength);
97
98     if (TargetLinkAddress) {
99         (ULONG_PTR)DataBuffer += ProtoAddressLength;
100         /* Target hardware address */
101         RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength);
102         (ULONG_PTR)DataBuffer += LinkAddressLength;
103     } else
104         /* Don't care about target hardware address */
105         (ULONG_PTR)DataBuffer += (ProtoAddressLength + LinkAddressLength);
106
107     /* Target protocol address */
108     RtlCopyMemory(DataBuffer, TargetProtoAddress, ProtoAddressLength);
109
110     return NdisPacket;
111 }
112
113
114 VOID ARPTransmitComplete(
115     PVOID Context,
116     PNDIS_PACKET NdisPacket,
117     NDIS_STATUS NdisStatus)
118 /*
119  * FUNCTION: ARP request transmit completion handler
120  * ARGUMENTS:
121  *     Context    = Pointer to context information (IP_INTERFACE)
122  *     Packet     = Pointer to NDIS packet that was sent
123  *     NdisStatus = NDIS status of operation
124  * NOTES:
125  *    This routine is called when an ARP request has been sent
126  */
127 {
128     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
129
130     FreeNdisPacket(NdisPacket);
131 }
132
133
134 BOOLEAN ARPTransmit(
135     PIP_ADDRESS Address,
136     PNET_TABLE_ENTRY NTE)
137 /*
138  * FUNCTION: Creates an ARP request and transmits it on a network
139  * ARGUMENTS:
140  *     Address = Pointer to IP address to resolve
141  *     NTE     = Pointer to net table entru to use for transmitting request
142  * RETURNS:
143  *     TRUE if the request was successfully sent, FALSE if not
144  */
145 {
146     PIP_INTERFACE Interface;
147     PNDIS_PACKET NdisPacket;
148     UCHAR ProtoAddrLen;
149     USHORT ProtoType;
150
151     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
152
153     Interface = NTE->Interface;
154
155     switch (Address->Type) {
156         case IP_ADDRESS_V4:
157             ProtoType    = (USHORT)ETYPE_IPv4; /* IPv4 */
158             ProtoAddrLen = 4;                  /* Length of IPv4 address */
159             break;
160         case IP_ADDRESS_V6:
161             ProtoType    = (USHORT)ETYPE_IPv6; /* IPv6 */
162             ProtoAddrLen = 16;                 /* Length of IPv6 address */
163             break;
164         default:
165             /* Should not happen */
166             return FALSE;
167     }
168
169     NdisPacket = PrepareARPPacket(
170         WN2H(0x0001),                    /* FIXME: Ethernet only */
171         ProtoType,                       /* Protocol type */
172         (UCHAR)Interface->AddressLength, /* Hardware address length */
173         (UCHAR)ProtoAddrLen,             /* Protocol address length */
174         Interface->Address,              /* Sender's (local) hardware address */
175         &NTE->Address->Address,          /* Sender's (local) protocol address */
176         NULL,                            /* Don't care */
177         &Address->Address,               /* Target's (remote) protocol address */
178         ARP_OPCODE_REQUEST);             /* ARP request */
179
180     PC(NdisPacket)->DLComplete = ARPTransmitComplete;
181
182     (*Interface->Transmit)(Interface->Context, NdisPacket,
183         MaxLLHeaderSize, NULL, LAN_PROTO_ARP);
184
185     return TRUE;
186 }
187
188
189 VOID ARPReceive(
190     PVOID Context,
191     PIP_PACKET Packet)
192 /*
193  * FUNCTION: Receives an ARP packet
194  * ARGUMENTS:
195  *     Context = Pointer to context information (IP_INTERFACE)
196  *     Packet  = Pointer to packet
197  */
198 {
199     PARP_HEADER Header;
200     PIP_ADDRESS Address;
201     PVOID SenderHWAddress;
202     PVOID SenderProtoAddress;
203     PVOID TargetProtoAddress;
204     PADDRESS_ENTRY ADE;
205     PNEIGHBOR_CACHE_ENTRY NCE;
206     PNDIS_PACKET NdisPacket;
207     PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
208
209     TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
210
211     Header = (PARP_HEADER)Packet->Header;
212
213     /* FIXME: Ethernet only */
214     if (WN2H(Header->HWType) != 1) {
215         TI_DbgPrint(DEBUG_ARP, ("Unknown ARP hardware type (0x%X).\n", WN2H(Header->HWType)));
216         return;
217     }
218
219     /* Check protocol type */
220     if (Header->ProtoType != ETYPE_IPv4) {
221         TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
222         return;
223     }
224
225     SenderHWAddress    = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
226     SenderProtoAddress = (PVOID)((ULONG_PTR)SenderHWAddress + Header->HWAddrLen);
227
228     /* Check if we have the target protocol address */
229
230     TargetProtoAddress = (PVOID)((ULONG_PTR)SenderProtoAddress +
231         Header->ProtoAddrLen + Header->HWAddrLen);
232
233     Address = AddrBuildIPv4(*((PULONG)TargetProtoAddress));
234     ADE = IPLocateADE(Address, ADE_UNICAST);
235     if (!ADE) {
236         TI_DbgPrint(DEBUG_ARP, ("Target address (0x%X) is not mine.\n", *((PULONG)TargetProtoAddress)));
237         return;
238     }
239
240     /* Check if we know the sender */
241
242     AddrInitIPv4(Address, *((PULONG)SenderProtoAddress));
243     NCE = NBLocateNeighbor(Address);
244     if (NCE) {
245         DereferenceObject(Address);
246         /* We know the sender. Update the hardware address 
247            and state in our neighbor address cache */
248         NBUpdateNeighbor(NCE, SenderHWAddress, NUD_REACHABLE);
249     } else {
250         /* The packet had our protocol address as target. The sender
251            may want to communicate with us soon, so add his address
252            to our address cache */
253         NCE = NBAddNeighbor(Interface, Address, SenderHWAddress,
254             Header->HWAddrLen, NUD_REACHABLE);
255     }
256     if (NCE)
257         DereferenceObject(NCE)
258
259     if (Header->Opcode != ARP_OPCODE_REQUEST)
260         return;
261     
262     /* This is a request for our address. Swap the addresses and
263        send an ARP reply back to the sender */
264     NdisPacket = PrepareARPPacket(
265         Header->HWType,                  /* Hardware type */
266         Header->ProtoType,               /* Protocol type */
267         (UCHAR)Interface->AddressLength, /* Hardware address length */
268         (UCHAR)Header->ProtoAddrLen,     /* Protocol address length */
269         Interface->Address,              /* Sender's (local) hardware address */
270         &ADE->Address->Address,          /* Sender's (local) protocol address */
271         SenderHWAddress,                 /* Target's (remote) hardware address */
272         SenderProtoAddress,              /* Target's (remote) protocol address */
273         ARP_OPCODE_REPLY);               /* ARP reply */
274     if (NdisPacket) {
275         PC(NdisPacket)->DLComplete = ARPTransmitComplete;
276         (*Interface->Transmit)(Interface->Context,
277                                NdisPacket,
278                                MaxLLHeaderSize,
279                                SenderHWAddress,
280                                LAN_PROTO_ARP);
281     }
282 }
283
284 /* EOF */