[6319] | 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 |
---|