source: ReferenceDesigns/w3_802.11/c/wlan_mac_high_framework/wlan_exp_ip_udp/wlan_exp_ip_udp_arp.c

Last change on this file was 6319, checked in by chunter, 5 years ago

1.8.0 release wlan-mac-se

File size: 12.2 KB
Line 
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//
23static arp_cache_entry_t ETH_arp_cache[WLAN_EXP_IP_UDP_NUM_ARP_ENTRIES];
24
25void 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 ******************************************************************************/
38void 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 *****************************************************************************/
61int 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 *****************************************************************************/
129void 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 *****************************************************************************/
207int 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 *****************************************************************************/
254int 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
Note: See TracBrowser for help on using the repository browser.