Merge trunk head (r43756)
[reactos.git] / reactos / lib / drivers / ip / network / 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
11 #include "precomp.h"
12
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
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
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
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 PVOID SenderHWAddress;
189 PVOID SenderProtoAddress;
190 PVOID TargetProtoAddress;
191 PNEIGHBOR_CACHE_ENTRY NCE;
192 PNDIS_PACKET NdisPacket;
193 PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
194
195 TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
196
197 Header = (PARP_HEADER)Packet->Header;
198
199 /* FIXME: Ethernet only */
200 if (WN2H(Header->HWType) != 1) {
201 TI_DbgPrint(DEBUG_ARP, ("Unknown ARP hardware type (0x%X).\n", WN2H(Header->HWType)));
202 return;
203 }
204
205 /* Check protocol type */
206 if (Header->ProtoType != ETYPE_IPv4) {
207 TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
208 return;
209 }
210
211 SenderHWAddress = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
212 SenderProtoAddress = (PVOID)((ULONG_PTR)SenderHWAddress + Header->HWAddrLen);
213 TargetProtoAddress = (PVOID)((ULONG_PTR)SenderProtoAddress + Header->ProtoAddrLen + Header->HWAddrLen);
214
215 AddrInitIPv4(&DstAddress, *((PULONG)TargetProtoAddress));
216 if (!AddrIsEqual(&DstAddress, &Interface->Unicast))
217 return;
218
219 AddrInitIPv4(&SrcAddress, *((PULONG)SenderProtoAddress));
220
221 /* Check if we know the sender */
222 NCE = NBLocateNeighbor(&SrcAddress);
223 if (NCE) {
224 /* We know the sender. Update the hardware address
225 and state in our neighbor address cache */
226 NBUpdateNeighbor(NCE, SenderHWAddress, 0);
227 } else {
228 /* The packet had our protocol address as target. The sender
229 may want to communicate with us soon, so add his address
230 to our address cache */
231 NCE = NBAddNeighbor(Interface, &SrcAddress, SenderHWAddress,
232 Header->HWAddrLen, 0, ARP_TIMEOUT);
233 }
234
235 if (Header->Opcode != ARP_OPCODE_REQUEST)
236 return;
237
238 /* This is a request for our address. Swap the addresses and
239 send an ARP reply back to the sender */
240 NdisPacket = PrepareARPPacket(
241 Interface,
242 Header->HWType, /* Hardware type */
243 Header->ProtoType, /* Protocol type */
244 (UCHAR)Interface->AddressLength, /* Hardware address length */
245 (UCHAR)Header->ProtoAddrLen, /* Protocol address length */
246 Interface->Address, /* Sender's (local) hardware address */
247 &Interface->Unicast.Address.IPv4Address,/* Sender's (local) protocol address */
248 SenderHWAddress, /* Target's (remote) hardware address */
249 SenderProtoAddress, /* Target's (remote) protocol address */
250 ARP_OPCODE_REPLY); /* ARP reply */
251 if (NdisPacket) {
252 PC(NdisPacket)->DLComplete = ARPTransmitComplete;
253 (*Interface->Transmit)(Interface->Context,
254 NdisPacket,
255 0,
256 SenderHWAddress,
257 LAN_PROTO_ARP);
258 }
259
260 Packet->Free(Packet);
261 }
262
263 /* EOF */