1 | /** @file wlan_exp_ip_udp_arp.c |
---|
2 | * @brief Mango wlan_exp IP/UDP Library (Internal Structures / Functions) |
---|
3 | * |
---|
4 | * @copyright Copyright 2014-2019, Mango Communications. All rights reserved. |
---|
5 | * Distributed under the Mango Reference Design license (https://mangocomm.com/802.11/license) |
---|
6 | */ |
---|
7 | |
---|
8 | #include "wlan_mac_high_sw_config.h" |
---|
9 | |
---|
10 | #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP |
---|
11 | #include "string.h" |
---|
12 | |
---|
13 | #include "wlan_exp_ip_udp.h" |
---|
14 | #include "wlan_exp_ip_udp_arp.h" |
---|
15 | #include "wlan_exp_transport.h" |
---|
16 | |
---|
17 | #include "wlan_mac_queue.h" |
---|
18 | |
---|
19 | #define WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES 10 // Number of ARP entries (global pool) |
---|
20 | |
---|
21 | // ARP Table data structures |
---|
22 | // |
---|
23 | static arp_cache_entry_t ETH_arp_cache[WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES]; |
---|
24 | |
---|
25 | void arp_reply(eth_rx_queue_buffer_t* eth_rx_queue_buffer, eth_tx_queue_buffer_t* eth_tx_queue_buffer); |
---|
26 | |
---|
27 | |
---|
28 | |
---|
29 | /*****************************************************************************/ |
---|
30 | /** |
---|
31 | * Initialize the ARP cache structure |
---|
32 | * |
---|
33 | * @param None |
---|
34 | * |
---|
35 | * @return None |
---|
36 | * |
---|
37 | ******************************************************************************/ |
---|
38 | void arp_init_cache() { |
---|
39 | u32 i; |
---|
40 | |
---|
41 | // Initialize ARP table |
---|
42 | for (i = 0; i < WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES; i++) { |
---|
43 | // Zero out the entry |
---|
44 | bzero((void *)&(ETH_arp_cache[i]), sizeof(arp_cache_entry_t)); |
---|
45 | } |
---|
46 | } |
---|
47 | |
---|
48 | |
---|
49 | /*****************************************************************************/ |
---|
50 | /** |
---|
51 | * Process the ARP packet |
---|
52 | * |
---|
53 | * @param eth_rx_queue_buffer - Ethernet packet |
---|
54 | * |
---|
55 | * @return int - Always returns 0 since we don't want higher level transports |
---|
56 | * to process this packet |
---|
57 | * |
---|
58 | * @note This function assumes that both Ethernet device and buffer are valid. |
---|
59 | * |
---|
60 | *****************************************************************************/ |
---|
61 | int arp_process_packet(eth_rx_queue_buffer_t* eth_rx_queue_buffer, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { |
---|
62 | |
---|
63 | arp_ipv4_packet_t* arp; |
---|
64 | u8 my_ip_addr[IP_ADDR_LEN]; |
---|
65 | |
---|
66 | arp = (arp_ipv4_packet_t*)((void*)(eth_rx_queue_buffer->pkt) + sizeof(ethernet_header_t)); |
---|
67 | eth_get_ip_addr(my_ip_addr); |
---|
68 | |
---|
69 | // Process the ARP packet |
---|
70 | // - If the ARP is a request to the node, then update the ARP table and send a reply |
---|
71 | // - If the ARP is a reply, then update the ARP table |
---|
72 | // |
---|
73 | // NOTE: The library does not currently process gratuitous ARPs since there are a limited number |
---|
74 | // of ARP table entries. However, this functionality would be easy to add in. |
---|
75 | // |
---|
76 | if ((Xil_Ntohs(arp->htype) == ARP_HTYPE_ETH) && // Hardware type is "Ethernet" |
---|
77 | (Xil_Ntohs(arp->ptype) == ETHERTYPE_IP_V4) && // Protocol type is "IP v4" |
---|
78 | (arp->hlen == ETH_ADDR_LEN) && // Hardware length is "Ethernet" |
---|
79 | (arp->plen == IP_ADDR_LEN) ) { // Protocol length is "IP v4" |
---|
80 | |
---|
81 | // |
---|
82 | // |
---|
83 | // !!! TBD !!! - Future addition: To process gratuitous ARPs, check: |
---|
84 | // - ARP Request and target_paddr == sender_paddr and target_haddr == {0, 0, 0, 0, 0, 0} |
---|
85 | // - ARP Reply and target_paddr == sender_paddr and target_haddr == sender_haddr |
---|
86 | // |
---|
87 | // |
---|
88 | |
---|
89 | // Check the ARP is for the node |
---|
90 | // NOTE: For ARP requests, the target haddr is ignored. |
---|
91 | // |
---|
92 | if ((arp->target_paddr[0] == my_ip_addr[0]) && // IP address matches node IP address |
---|
93 | (arp->target_paddr[1] == my_ip_addr[1]) && |
---|
94 | (arp->target_paddr[2] == my_ip_addr[2]) && |
---|
95 | (arp->target_paddr[3] == my_ip_addr[3]) ) { |
---|
96 | |
---|
97 | // Update the ARP table regardless of whether this is a request or a reply |
---|
98 | arp_update_cache(arp->sender_haddr, arp->sender_paddr); |
---|
99 | |
---|
100 | // Process the ARP operation |
---|
101 | // NOTE: In the case of an ARP reply, we have already updated the ARP cache, so there is nothing |
---|
102 | // further to be done. Therefore, we are just using an if statement to check if this is |
---|
103 | // an ARP request. If needed, this can be changed to a case statement to process other ARP |
---|
104 | // operations. |
---|
105 | // |
---|
106 | if (Xil_Ntohs(arp->oper) == ARP_REQUEST) { |
---|
107 | // Send an ARP reply |
---|
108 | arp_reply(eth_rx_queue_buffer, eth_tx_queue_buffer); |
---|
109 | } |
---|
110 | } |
---|
111 | } |
---|
112 | |
---|
113 | return 0; // Upper layer stacks should not process this packet so return zero bytes |
---|
114 | } |
---|
115 | |
---|
116 | |
---|
117 | |
---|
118 | /*****************************************************************************/ |
---|
119 | /** |
---|
120 | * Send an ARP Reply |
---|
121 | * |
---|
122 | * @param eth_rx_queue_buffer - received ARP packet |
---|
123 | * |
---|
124 | * @return None |
---|
125 | * |
---|
126 | * @note This function assumes that both Ethernet device and buffer are valid. |
---|
127 | * |
---|
128 | *****************************************************************************/ |
---|
129 | void arp_reply(eth_rx_queue_buffer_t* eth_rx_queue_buffer, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { |
---|
130 | u32 i; |
---|
131 | |
---|
132 | arp_ipv4_packet_t* request; |
---|
133 | |
---|
134 | u8 eth_ip_addr[IP_ADDR_LEN]; |
---|
135 | u8 eth_hw_addr[ETH_ADDR_LEN]; |
---|
136 | |
---|
137 | ethernet_header_t* eth_hdr; |
---|
138 | |
---|
139 | request = (arp_ipv4_packet_t*)((void*)eth_rx_queue_buffer->pkt + sizeof(ethernet_header_t)); |
---|
140 | eth_get_ip_addr(eth_ip_addr); |
---|
141 | eth_get_hw_addr(eth_hw_addr); |
---|
142 | |
---|
143 | u32 arp_size = ETH_HEADER_LEN + arp_ipv4_packet_t_LEN; |
---|
144 | |
---|
145 | // If the packet was successfully allocated |
---|
146 | if (eth_tx_queue_buffer != NULL) { |
---|
147 | |
---|
148 | |
---|
149 | // Initialize the Ethernet header |
---|
150 | // NOTE: We will not use a UDP socket to send this packet, since this reply occurs at a |
---|
151 | // lower level in the protocol stack than a UDP socket. |
---|
152 | // |
---|
153 | eth_hdr = (ethernet_header_t*)eth_tx_queue_buffer->seg0; |
---|
154 | memcpy(eth_hdr->dest_mac_addr, request->sender_haddr, ETH_ADDR_SIZE); |
---|
155 | memcpy(eth_hdr->src_mac_addr, eth_hw_addr, ETH_ADDR_SIZE); |
---|
156 | eth_hdr->ethertype = Xil_Htons(ETHERTYPE_ARP); |
---|
157 | |
---|
158 | // Get the pointer to the ARP reply packet |
---|
159 | arp_ipv4_packet_t* arp_reply_packet = (arp_ipv4_packet_t*)(eth_tx_queue_buffer->seg0 + sizeof(ethernet_header_t)); |
---|
160 | |
---|
161 | // Populate the ARP reply |
---|
162 | arp_reply_packet->htype = Xil_Htons(ARP_HTYPE_ETH); |
---|
163 | arp_reply_packet->ptype = Xil_Htons(ETHERTYPE_IP_V4); |
---|
164 | arp_reply_packet->hlen = ETH_ADDR_LEN; |
---|
165 | arp_reply_packet->plen = IP_ADDR_LEN; |
---|
166 | arp_reply_packet->oper = Xil_Htons(ARP_REPLY); |
---|
167 | |
---|
168 | for (i = 0; i < ETH_ADDR_LEN; i++) { |
---|
169 | arp_reply_packet->sender_haddr[i] = eth_hw_addr[i]; |
---|
170 | arp_reply_packet->target_haddr[i] = request->sender_haddr[i]; |
---|
171 | } |
---|
172 | |
---|
173 | for (i = 0; i < IP_ADDR_LEN ; i++) { |
---|
174 | arp_reply_packet->sender_paddr[i] = eth_ip_addr[i]; |
---|
175 | arp_reply_packet->target_paddr[i] = request->sender_paddr[i]; |
---|
176 | } |
---|
177 | |
---|
178 | // By setting the length of segment 0, we inform the calling context we have filled |
---|
179 | // in an Ethernet frame that needs transmission |
---|
180 | eth_tx_queue_buffer->seg0_len = arp_size; |
---|
181 | |
---|
182 | } else { |
---|
183 | xil_printf("Error: Provided with NULL eth_tx_queue_buffer_t*\n"); |
---|
184 | } |
---|
185 | } |
---|
186 | |
---|
187 | /*****************************************************************************/ |
---|
188 | /** |
---|
189 | * Get the Hardware address associated with the Ethernet device / IP address from |
---|
190 | * the ARP cache. |
---|
191 | * |
---|
192 | * @param eth_dev_num - Ethernet device to match in the cache |
---|
193 | * @param hw_addr - Hardware address (to be returned from the cache) |
---|
194 | * @param ip_addr - IP address to match in the cache |
---|
195 | * |
---|
196 | * @return int - Status of the command: |
---|
197 | * WLAN_SUCCESS - Command completed successfully |
---|
198 | * WLAN_FAILURE - There was an error in the command |
---|
199 | * |
---|
200 | * @note The reason for the "strange" order of the arguments is to maintain |
---|
201 | * consistency when specifying HW address and IP address (ie all functions |
---|
202 | * required HW address then IP address when both are part of the arguments). |
---|
203 | * Since both addresses are (u8 *), the compiler cannot tell them apart |
---|
204 | * which makes it easy to get them reversed. |
---|
205 | * |
---|
206 | *****************************************************************************/ |
---|
207 | int arp_get_hw_addr(u8* hw_addr, u8* ip_addr) { |
---|
208 | |
---|
209 | int i, j; |
---|
210 | |
---|
211 | // Look through the ARP table |
---|
212 | for (i = 0; i < WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES; i++) { |
---|
213 | |
---|
214 | // If an entry is in use, then check the IP address |
---|
215 | if (ETH_arp_cache[i].state == ARP_TABLE_USED) { |
---|
216 | |
---|
217 | // If the IP address / eth_dev_num matches, then copy the hardware address |
---|
218 | if ((ETH_arp_cache[i].paddr[0] == ip_addr[0]) && |
---|
219 | (ETH_arp_cache[i].paddr[1] == ip_addr[1]) && |
---|
220 | (ETH_arp_cache[i].paddr[2] == ip_addr[2]) && |
---|
221 | (ETH_arp_cache[i].paddr[3] == ip_addr[3])) { |
---|
222 | |
---|
223 | // Copy the hardware address |
---|
224 | for (j = 0; j < ETH_ADDR_LEN; j++) { |
---|
225 | hw_addr[j] = ETH_arp_cache[i].haddr[j]; |
---|
226 | } |
---|
227 | |
---|
228 | return WLAN_SUCCESS; |
---|
229 | } |
---|
230 | } |
---|
231 | } |
---|
232 | |
---|
233 | return WLAN_FAILURE; |
---|
234 | } |
---|
235 | |
---|
236 | |
---|
237 | |
---|
238 | /*****************************************************************************/ |
---|
239 | /** |
---|
240 | * Update the ARP cache |
---|
241 | * |
---|
242 | * This cache uses Ethernet device and IP address as keys to index hardware addresses. |
---|
243 | * |
---|
244 | * @param hw_addr - Hardware address |
---|
245 | * @param ip_addr - IP address |
---|
246 | * |
---|
247 | * @return int - Status of the command: |
---|
248 | * 0 - Command completed successfully |
---|
249 | * -1 - There was an error in the command |
---|
250 | * |
---|
251 | * @note This function assumes that both socket and buffer are valid. |
---|
252 | * |
---|
253 | *****************************************************************************/ |
---|
254 | int arp_update_cache(u8* hw_addr, u8* ip_addr) { |
---|
255 | |
---|
256 | int i, j; |
---|
257 | int first_unused_entry = -1; |
---|
258 | int oldest_entry = -1; |
---|
259 | int entry_age = -1; |
---|
260 | int entry_to_use = -1; |
---|
261 | |
---|
262 | // Look through the ARP table: |
---|
263 | // - Check that current IP address to see if entry already exists for the Ethernet device; |
---|
264 | // - Update the hw address |
---|
265 | // - Set age to zero |
---|
266 | // - Update the age of all entries that are being used |
---|
267 | // - Record the first unused entry |
---|
268 | // - Record the oldest entry |
---|
269 | // |
---|
270 | for (i = 0; i < WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES; i++) { |
---|
271 | if (ETH_arp_cache[i].state == ARP_TABLE_USED) { |
---|
272 | |
---|
273 | // If this entry is older than the current oldest, then record it and update the age |
---|
274 | if (entry_age < (int)ETH_arp_cache[i].age) { |
---|
275 | oldest_entry = i; |
---|
276 | entry_age = ETH_arp_cache[i].age; |
---|
277 | } |
---|
278 | |
---|
279 | // Update the age of the used entry |
---|
280 | ETH_arp_cache[i].age += 1; |
---|
281 | |
---|
282 | // If the IP address / eth_dev_num matches, then copy the hardware address |
---|
283 | if ((ETH_arp_cache[i].paddr[0] == ip_addr[0]) && |
---|
284 | (ETH_arp_cache[i].paddr[1] == ip_addr[1]) && |
---|
285 | (ETH_arp_cache[i].paddr[2] == ip_addr[2]) && |
---|
286 | (ETH_arp_cache[i].paddr[3] == ip_addr[3])) { |
---|
287 | |
---|
288 | // Copy the hardware address |
---|
289 | for (j = 0; j < ETH_ADDR_LEN; j++) { |
---|
290 | ETH_arp_cache[i].haddr[j] = hw_addr[j]; |
---|
291 | } |
---|
292 | |
---|
293 | // Set age to zero |
---|
294 | ETH_arp_cache[i].age = 0; |
---|
295 | |
---|
296 | // We are done updating the table |
---|
297 | return WLAN_SUCCESS; |
---|
298 | } |
---|
299 | |
---|
300 | } else { |
---|
301 | // Record first unused entry |
---|
302 | if (first_unused_entry < 0) { |
---|
303 | first_unused_entry = i; |
---|
304 | } |
---|
305 | } |
---|
306 | } |
---|
307 | |
---|
308 | // If we reach, here we need to add the entry to the table |
---|
309 | // - If there is an unused entry, then we should use that |
---|
310 | // - If there are no unused entries, then we should use the oldest entry (LRU replacement policy) |
---|
311 | // |
---|
312 | if (first_unused_entry != -1) { |
---|
313 | entry_to_use = first_unused_entry; |
---|
314 | |
---|
315 | // Mark unused entry as used |
---|
316 | ETH_arp_cache[entry_to_use].state = ARP_TABLE_USED; |
---|
317 | } else { |
---|
318 | entry_to_use = oldest_entry; |
---|
319 | } |
---|
320 | |
---|
321 | // Copy the IP / HW addresses and eth_dev_num to entry |
---|
322 | if (entry_to_use != -1) { |
---|
323 | // Copy IP address |
---|
324 | for (i = 0; i < IP_ADDR_LEN; i++) { |
---|
325 | ETH_arp_cache[entry_to_use].paddr[i] = ip_addr[i]; |
---|
326 | } |
---|
327 | |
---|
328 | // Copy HW address |
---|
329 | for (i = 0; i < ETH_ADDR_LEN; i++) { |
---|
330 | ETH_arp_cache[entry_to_use].haddr[i] = hw_addr[i]; |
---|
331 | } |
---|
332 | |
---|
333 | } else { |
---|
334 | return WLAN_FAILURE; |
---|
335 | } |
---|
336 | |
---|
337 | return WLAN_SUCCESS; |
---|
338 | } |
---|
339 | #endif //WLAN_SW_CONFIG_ENABLE_WLAN_EXP |
---|