adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2001-2002, Adam Dunkels. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by Adam Dunkels. |
| 16 | * 4. The name of the author may not be used to endorse or promote |
| 17 | * products derived from this software without specific prior |
| 18 | * written permission. |
| 19 | * |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| 21 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 24 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| 26 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 28 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | * |
| 32 | * This file is part of the uIP TCP/IP stack. |
| 33 | * |
adamdunkels | 3ea09e6 | 2003-08-24 22:40:46 +0000 | [diff] [blame^] | 34 | * $Id: uip_arp.c,v 1.6 2003/08/24 22:40:46 adamdunkels Exp $ |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 35 | * |
| 36 | */ |
| 37 | |
| 38 | |
| 39 | #include "uip_arp.h" |
| 40 | |
| 41 | struct arp_hdr { |
| 42 | struct uip_eth_hdr ethhdr; |
| 43 | u16_t hwtype; |
| 44 | u16_t protocol; |
| 45 | u8_t hwlen; |
| 46 | u8_t protolen; |
| 47 | u16_t opcode; |
| 48 | struct uip_eth_addr shwaddr; |
| 49 | u16_t sipaddr[2]; |
| 50 | struct uip_eth_addr dhwaddr; |
| 51 | u16_t dipaddr[2]; |
| 52 | }; |
| 53 | |
| 54 | struct ethip_hdr { |
| 55 | struct uip_eth_hdr ethhdr; |
| 56 | /* IP header. */ |
| 57 | u8_t vhl, |
| 58 | tos, |
| 59 | len[2], |
| 60 | ipid[2], |
| 61 | ipoffset[2], |
| 62 | ttl, |
| 63 | proto; |
| 64 | u16_t ipchksum; |
| 65 | u16_t srcipaddr[2], |
| 66 | destipaddr[2]; |
| 67 | }; |
| 68 | |
| 69 | #define ARP_REQUEST 1 |
| 70 | #define ARP_REPLY 2 |
| 71 | |
| 72 | #define ARP_HWTYPE_ETH 1 |
| 73 | |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 74 | struct arp_entry { |
| 75 | u16_t ipaddr[2]; |
| 76 | struct uip_eth_addr ethaddr; |
| 77 | u8_t time; |
| 78 | }; |
| 79 | |
adamdunkels | c5ff285 | 2003-06-30 20:36:28 +0000 | [diff] [blame] | 80 | struct uip_eth_addr uip_ethaddr = {{UIP_ETHADDR0, |
| 81 | UIP_ETHADDR1, |
| 82 | UIP_ETHADDR2, |
| 83 | UIP_ETHADDR3, |
| 84 | UIP_ETHADDR4, |
| 85 | UIP_ETHADDR5}}; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 86 | |
| 87 | static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; |
| 88 | static u16_t ipaddr[2]; |
| 89 | static u8_t i, c; |
| 90 | |
| 91 | static u8_t arptime; |
| 92 | static u8_t tmpage; |
| 93 | |
| 94 | #define BUF ((struct arp_hdr *)&uip_buf[0]) |
| 95 | #define IPBUF ((struct ethip_hdr *)&uip_buf[0]) |
| 96 | /*-----------------------------------------------------------------------------------*/ |
| 97 | void |
| 98 | uip_arp_init(void) |
| 99 | { |
| 100 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 101 | memset(arp_table[i].ipaddr, 0, 4); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 102 | } |
| 103 | } |
| 104 | /*-----------------------------------------------------------------------------------*/ |
| 105 | void |
| 106 | uip_arp_timer(void) |
| 107 | { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 108 | struct arp_entry *tabptr; |
| 109 | |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 110 | ++arptime; |
| 111 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 112 | tabptr = &arp_table[i]; |
| 113 | if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && |
| 114 | arptime - tabptr->time >= UIP_ARP_MAXAGE) { |
| 115 | memset(tabptr->ipaddr, 0, 4); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | |
| 119 | } |
| 120 | /*-----------------------------------------------------------------------------------*/ |
| 121 | static void |
| 122 | uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) |
| 123 | { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 124 | register struct arp_entry *tabptr; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 125 | /* Walk through the ARP mapping table and try to find an entry to |
| 126 | update. If none is found, the IP -> MAC address mapping is |
| 127 | inserted in the ARP table. */ |
| 128 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 129 | |
| 130 | tabptr = &arp_table[i]; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 131 | /* Only check those entries that are actually in use. */ |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 132 | if(tabptr->ipaddr[0] != 0 && |
| 133 | tabptr->ipaddr[1] != 0) { |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 134 | |
| 135 | /* Check if the source IP address of the incoming packet matches |
| 136 | the IP address in this ARP table entry. */ |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 137 | if(ipaddr[0] == tabptr->ipaddr[0] && |
| 138 | ipaddr[1] == tabptr->ipaddr[1]) { |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 139 | |
| 140 | /* An old entry found, update this and return. */ |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 141 | memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); |
| 142 | tabptr->time = arptime; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 143 | |
| 144 | return; |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /* If we get here, no existing ARP table entry was found, so we |
| 150 | create one. */ |
| 151 | |
| 152 | /* First, we try to find an unused entry in the ARP table. */ |
| 153 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 154 | tabptr = &arp_table[i]; |
| 155 | if(tabptr->ipaddr[0] == 0 && |
| 156 | tabptr->ipaddr[1] == 0) { |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 157 | break; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /* If no unused entry is found, we try to find the oldest entry and |
| 162 | throw it away. */ |
| 163 | if(i == UIP_ARPTAB_SIZE) { |
| 164 | tmpage = 0; |
| 165 | c = 0; |
| 166 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 167 | tabptr = &arp_table[i]; |
| 168 | if(arptime - tabptr->time > tmpage) { |
| 169 | tmpage = arptime - tabptr->time; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 170 | c = i; |
| 171 | } |
| 172 | } |
| 173 | i = c; |
| 174 | } |
| 175 | |
| 176 | /* Now, i is the ARP table entry which we will fill with the new |
| 177 | information. */ |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 178 | memcpy(tabptr->ipaddr, ipaddr, 4); |
| 179 | memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 180 | tabptr->time = arptime; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 181 | } |
| 182 | /*-----------------------------------------------------------------------------------*/ |
| 183 | void |
| 184 | uip_arp_ipin(void) |
| 185 | { |
| 186 | |
| 187 | /* Only insert/update an entry if the source IP address of the |
| 188 | incoming IP packet comes from a host on the local network. */ |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 189 | if((IPBUF->srcipaddr[0] & uip_arp_netmask[0]) != |
| 190 | (uip_hostaddr[0] & uip_arp_netmask[0])) { |
| 191 | return; |
| 192 | } |
| 193 | if((IPBUF->srcipaddr[1] & uip_arp_netmask[1]) != |
| 194 | (uip_hostaddr[1] & uip_arp_netmask[1])) { |
| 195 | return; |
| 196 | } |
| 197 | uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src)); |
| 198 | |
| 199 | return; |
| 200 | } |
| 201 | /*-----------------------------------------------------------------------------------*/ |
| 202 | void |
| 203 | uip_arp_arpin(void) |
| 204 | { |
| 205 | |
| 206 | if(uip_len < sizeof(struct arp_hdr)) { |
| 207 | uip_len = 0; |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | uip_len = 0; |
| 212 | |
| 213 | switch(BUF->opcode) { |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 214 | case HTONS(ARP_REQUEST): |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 215 | /* ARP request. If it asked for our address, we send out a |
| 216 | reply. */ |
| 217 | if(BUF->dipaddr[0] == uip_hostaddr[0] && |
| 218 | BUF->dipaddr[1] == uip_hostaddr[1]) { |
| 219 | /* The reply opcode is 2. */ |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 220 | BUF->opcode = HTONS(2); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 221 | |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 222 | memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); |
| 223 | memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); |
| 224 | memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); |
| 225 | memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); |
adamdunkels | 3ea09e6 | 2003-08-24 22:40:46 +0000 | [diff] [blame^] | 226 | |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 227 | BUF->dipaddr[0] = BUF->sipaddr[0]; |
| 228 | BUF->dipaddr[1] = BUF->sipaddr[1]; |
| 229 | BUF->sipaddr[0] = uip_hostaddr[0]; |
| 230 | BUF->sipaddr[1] = uip_hostaddr[1]; |
| 231 | |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 232 | BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 233 | uip_len = sizeof(struct arp_hdr); |
| 234 | } |
| 235 | break; |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 236 | case HTONS(ARP_REPLY): |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 237 | /* ARP reply. We insert or update the ARP table if it was meant |
| 238 | for us. */ |
| 239 | if(BUF->dipaddr[0] == uip_hostaddr[0] && |
| 240 | BUF->dipaddr[1] == uip_hostaddr[1]) { |
| 241 | |
| 242 | uip_arp_update(BUF->sipaddr, &BUF->shwaddr); |
| 243 | } |
| 244 | break; |
| 245 | } |
| 246 | |
| 247 | return; |
| 248 | } |
| 249 | /*-----------------------------------------------------------------------------------*/ |
| 250 | void |
| 251 | uip_arp_out(void) |
| 252 | { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 253 | struct arp_entry *tabptr; |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 254 | /* Find the destination IP address in the ARP table and construct |
| 255 | the Ethernet header. If the destination IP addres isn't on the |
| 256 | local network, we use the default router's IP address instead. |
| 257 | |
| 258 | If not ARP table entry is found, we overwrite the original IP |
| 259 | packet with an ARP request for the IP address. */ |
| 260 | |
| 261 | /* Check if the destination address is on the local network. */ |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 262 | if((IPBUF->destipaddr[0] & uip_arp_netmask[0]) != |
| 263 | (uip_hostaddr[0] & uip_arp_netmask[0]) || |
| 264 | (IPBUF->destipaddr[1] & uip_arp_netmask[1]) != |
| 265 | (uip_hostaddr[1] & uip_arp_netmask[1])) { |
| 266 | /* Destination address was not on the local network, so we need to |
| 267 | use the default router's IP address instead of the destination |
| 268 | address when determining the MAC address. */ |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 269 | ipaddr[0] = uip_arp_draddr[0]; |
| 270 | ipaddr[1] = uip_arp_draddr[1]; |
| 271 | } else { |
| 272 | /* Else, we use the destination IP address. */ |
| 273 | ipaddr[0] = IPBUF->destipaddr[0]; |
| 274 | ipaddr[1] = IPBUF->destipaddr[1]; |
| 275 | } |
| 276 | |
| 277 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 278 | tabptr = &arp_table[i]; |
| 279 | if(ipaddr[0] == tabptr->ipaddr[0] && |
| 280 | ipaddr[1] == tabptr->ipaddr[1]) |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 281 | break; |
| 282 | } |
| 283 | |
| 284 | if(i == UIP_ARPTAB_SIZE) { |
| 285 | /* The destination address was not in our ARP table, so we |
| 286 | overwrite the IP packet with an ARP request. */ |
| 287 | |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 288 | memset(BUF->ethhdr.dest.addr, 0xff, 6); |
| 289 | memset(BUF->dhwaddr.addr, 0x00, 6); |
| 290 | memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); |
| 291 | memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 292 | |
| 293 | BUF->dipaddr[0] = ipaddr[0]; |
| 294 | BUF->dipaddr[1] = ipaddr[1]; |
| 295 | BUF->sipaddr[0] = uip_hostaddr[0]; |
| 296 | BUF->sipaddr[1] = uip_hostaddr[1]; |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 297 | BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ |
| 298 | BUF->hwtype = HTONS(ARP_HWTYPE_ETH); |
| 299 | BUF->protocol = HTONS(UIP_ETHTYPE_IP); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 300 | BUF->hwlen = 6; |
| 301 | BUF->protolen = 4; |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 302 | BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 303 | |
| 304 | uip_appdata = &uip_buf[40 + UIP_LLH_LEN]; |
| 305 | |
| 306 | uip_len = sizeof(struct arp_hdr); |
| 307 | return; |
| 308 | } |
| 309 | |
| 310 | /* Build an ethernet header. */ |
adamdunkels | 2366402 | 2003-08-05 13:51:50 +0000 | [diff] [blame] | 311 | memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6); |
| 312 | memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6); |
adamdunkels | 3ea09e6 | 2003-08-24 22:40:46 +0000 | [diff] [blame^] | 313 | |
adamdunkels | 47ec7fa | 2003-03-28 12:11:17 +0000 | [diff] [blame] | 314 | IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); |
adamdunkels | ca9ddcb | 2003-03-19 14:13:31 +0000 | [diff] [blame] | 315 | |
| 316 | uip_len += sizeof(struct uip_eth_hdr); |
| 317 | } |
| 318 | /*-----------------------------------------------------------------------------------*/ |
| 319 | |
| 320 | |
| 321 | |
| 322 | |