Free (GPLv2) TCP/IP stack developed by TASS Belgium

Fork of PicoTCP by Daniele Lacamera

Committer:
tass
Date:
Fri May 17 12:09:59 2013 +0000
Revision:
1:cfe8984a32b4
Parent:
libraries/picotcp/stack/pico_arp.c@0:d7f2341ab245
Update for smaller SOCKETQ

Who changed what in which revision?

UserRevisionLine numberNew contents of line
daniele 0:d7f2341ab245 1 /*********************************************************************
daniele 0:d7f2341ab245 2 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
daniele 0:d7f2341ab245 3 See LICENSE and COPYING for usage.
daniele 0:d7f2341ab245 4
daniele 0:d7f2341ab245 5 .
daniele 0:d7f2341ab245 6
daniele 0:d7f2341ab245 7 Authors: Daniele Lacamera
daniele 0:d7f2341ab245 8 *********************************************************************/
daniele 0:d7f2341ab245 9
daniele 0:d7f2341ab245 10
daniele 0:d7f2341ab245 11 #include "pico_config.h"
daniele 0:d7f2341ab245 12 #include "pico_arp.h"
daniele 0:d7f2341ab245 13 #include "pico_tree.h"
daniele 0:d7f2341ab245 14 #include "pico_ipv4.h"
daniele 0:d7f2341ab245 15 #include "pico_device.h"
daniele 0:d7f2341ab245 16 #include "pico_stack.h"
daniele 0:d7f2341ab245 17
daniele 0:d7f2341ab245 18 const uint8_t PICO_ETHADDR_ALL[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
daniele 0:d7f2341ab245 19 #define PICO_ARP_TIMEOUT 600000
daniele 0:d7f2341ab245 20 #define PICO_ARP_RETRY 300
daniele 0:d7f2341ab245 21
daniele 0:d7f2341ab245 22 #ifdef DEBUG_ARP
daniele 0:d7f2341ab245 23 #define arp_dbg dbg
daniele 0:d7f2341ab245 24 #else
daniele 0:d7f2341ab245 25 #define arp_dbg(...) do{}while(0)
daniele 0:d7f2341ab245 26 #endif
daniele 0:d7f2341ab245 27
daniele 0:d7f2341ab245 28 static struct pico_queue pending;
daniele 0:d7f2341ab245 29 static int pending_timer_on = 0;
daniele 0:d7f2341ab245 30
daniele 0:d7f2341ab245 31 void check_pending(unsigned long now, void *_unused)
daniele 0:d7f2341ab245 32 {
daniele 0:d7f2341ab245 33 struct pico_frame *f = pico_dequeue(&pending);
daniele 0:d7f2341ab245 34 if (!f) {
daniele 0:d7f2341ab245 35 pending_timer_on = 0;
daniele 0:d7f2341ab245 36 return;
daniele 0:d7f2341ab245 37 }
daniele 0:d7f2341ab245 38 if(pico_ethernet_send(f) > 0)
daniele 0:d7f2341ab245 39 pico_frame_discard(f);
daniele 0:d7f2341ab245 40 pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
daniele 0:d7f2341ab245 41 }
daniele 0:d7f2341ab245 42
daniele 0:d7f2341ab245 43
daniele 0:d7f2341ab245 44 struct
daniele 0:d7f2341ab245 45 __attribute__ ((__packed__))
daniele 0:d7f2341ab245 46 pico_arp_hdr
daniele 0:d7f2341ab245 47 {
daniele 0:d7f2341ab245 48 uint16_t htype;
daniele 0:d7f2341ab245 49 uint16_t ptype;
daniele 0:d7f2341ab245 50 uint8_t hsize;
daniele 0:d7f2341ab245 51 uint8_t psize;
daniele 0:d7f2341ab245 52 uint16_t opcode;
daniele 0:d7f2341ab245 53 uint8_t s_mac[PICO_SIZE_ETH];
daniele 0:d7f2341ab245 54 struct pico_ip4 src;
daniele 0:d7f2341ab245 55 uint8_t d_mac[PICO_SIZE_ETH];
daniele 0:d7f2341ab245 56 struct pico_ip4 dst;
daniele 0:d7f2341ab245 57 };
daniele 0:d7f2341ab245 58
daniele 0:d7f2341ab245 59
daniele 0:d7f2341ab245 60 #define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
daniele 0:d7f2341ab245 61
daniele 0:d7f2341ab245 62 /* Arp Entries for the tables. */
daniele 0:d7f2341ab245 63 struct pico_arp {
daniele 0:d7f2341ab245 64 /* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure,
daniele 0:d7f2341ab245 65 * due to in-place casting!!! */
daniele 0:d7f2341ab245 66 struct pico_eth eth;
daniele 0:d7f2341ab245 67 struct pico_ip4 ipv4;
daniele 0:d7f2341ab245 68 int arp_status;
daniele 0:d7f2341ab245 69 uint32_t timestamp;
daniele 0:d7f2341ab245 70 struct pico_device *dev;
daniele 0:d7f2341ab245 71 };
daniele 0:d7f2341ab245 72
daniele 0:d7f2341ab245 73
daniele 0:d7f2341ab245 74
daniele 0:d7f2341ab245 75 /*****************/
daniele 0:d7f2341ab245 76 /** ARP TREE **/
daniele 0:d7f2341ab245 77 /*****************/
daniele 0:d7f2341ab245 78
daniele 0:d7f2341ab245 79 /* Routing destination */
daniele 0:d7f2341ab245 80
daniele 0:d7f2341ab245 81 static int arp_compare(void * ka, void * kb)
daniele 0:d7f2341ab245 82 {
daniele 0:d7f2341ab245 83 struct pico_arp *a = ka, *b = kb;
daniele 0:d7f2341ab245 84 if (a->ipv4.addr < b->ipv4.addr)
daniele 0:d7f2341ab245 85 return -1;
daniele 0:d7f2341ab245 86 else if (a->ipv4.addr > b->ipv4.addr)
daniele 0:d7f2341ab245 87 return 1;
daniele 0:d7f2341ab245 88 return 0;
daniele 0:d7f2341ab245 89 }
daniele 0:d7f2341ab245 90
daniele 0:d7f2341ab245 91 PICO_TREE_DECLARE(arp_tree, arp_compare);
daniele 0:d7f2341ab245 92
daniele 0:d7f2341ab245 93 /*********************/
daniele 0:d7f2341ab245 94 /** END ARP TREE **/
daniele 0:d7f2341ab245 95 /*********************/
daniele 0:d7f2341ab245 96
daniele 0:d7f2341ab245 97 struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
daniele 0:d7f2341ab245 98 {
daniele 0:d7f2341ab245 99 struct pico_arp search, *found;
daniele 0:d7f2341ab245 100 search.ipv4.addr = dst->addr;
daniele 0:d7f2341ab245 101 found = pico_tree_findKey(&arp_tree,&search);
daniele 0:d7f2341ab245 102 if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
daniele 0:d7f2341ab245 103 return &found->eth;
daniele 0:d7f2341ab245 104 return NULL;
daniele 0:d7f2341ab245 105 }
daniele 0:d7f2341ab245 106
daniele 0:d7f2341ab245 107 struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
daniele 0:d7f2341ab245 108 {
daniele 0:d7f2341ab245 109 struct pico_arp* search;
daniele 0:d7f2341ab245 110 struct pico_tree_node * index;
daniele 0:d7f2341ab245 111 pico_tree_foreach(index,&arp_tree){
daniele 0:d7f2341ab245 112 search = index->keyValue;
daniele 0:d7f2341ab245 113 if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
daniele 0:d7f2341ab245 114 return &search->ipv4;
daniele 0:d7f2341ab245 115 }
daniele 0:d7f2341ab245 116 return NULL;
daniele 0:d7f2341ab245 117 }
daniele 0:d7f2341ab245 118
daniele 0:d7f2341ab245 119 struct pico_eth *pico_arp_get(struct pico_frame *f) {
daniele 0:d7f2341ab245 120 struct pico_eth *a4;
daniele 0:d7f2341ab245 121 struct pico_ip4 gateway;
daniele 0:d7f2341ab245 122 struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
daniele 0:d7f2341ab245 123 struct pico_ipv4_link *l;
daniele 0:d7f2341ab245 124
daniele 0:d7f2341ab245 125 l = pico_ipv4_link_get(&hdr->dst);
daniele 0:d7f2341ab245 126 if(l){
daniele 0:d7f2341ab245 127 //address belongs to ourself
daniele 0:d7f2341ab245 128 return &l->dev->eth->mac;
daniele 0:d7f2341ab245 129 }
daniele 0:d7f2341ab245 130
daniele 0:d7f2341ab245 131 gateway = pico_ipv4_route_get_gateway(&hdr->dst);
daniele 0:d7f2341ab245 132 /* check if dst is local (gateway = 0), or if to use gateway */
daniele 0:d7f2341ab245 133 if (gateway.addr != 0)
daniele 0:d7f2341ab245 134 a4 = pico_arp_lookup(&gateway); /* check if gateway ip mac in cache */
daniele 0:d7f2341ab245 135 else
daniele 0:d7f2341ab245 136 a4 = pico_arp_lookup(&hdr->dst); /* check if local ip mac in cache */
daniele 0:d7f2341ab245 137 if (!a4) {
daniele 0:d7f2341ab245 138 if (++f->failure_count < 4) {
daniele 0:d7f2341ab245 139 dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
daniele 0:d7f2341ab245 140 /* check if dst is local (gateway = 0), or if to use gateway */
daniele 0:d7f2341ab245 141 if (gateway.addr != 0)
daniele 0:d7f2341ab245 142 pico_arp_query(f->dev, &gateway); /* arp to gateway */
daniele 0:d7f2341ab245 143 else
daniele 0:d7f2341ab245 144 pico_arp_query(f->dev, &hdr->dst); /* arp to dst */
daniele 0:d7f2341ab245 145
daniele 0:d7f2341ab245 146 pico_enqueue(&pending, f);
daniele 0:d7f2341ab245 147 if (!pending_timer_on) {
daniele 0:d7f2341ab245 148 pending_timer_on++;
daniele 0:d7f2341ab245 149 pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
daniele 0:d7f2341ab245 150 }
daniele 0:d7f2341ab245 151 } else {
daniele 0:d7f2341ab245 152 dbg("ARP: Destination Unreachable\n");
daniele 0:d7f2341ab245 153 pico_notify_dest_unreachable(f);
daniele 0:d7f2341ab245 154 pico_frame_discard(f);
daniele 0:d7f2341ab245 155 }
daniele 0:d7f2341ab245 156 }
daniele 0:d7f2341ab245 157 return a4;
daniele 0:d7f2341ab245 158 }
daniele 0:d7f2341ab245 159
daniele 0:d7f2341ab245 160 #ifdef DEBUG_ARP
daniele 0:d7f2341ab245 161 void dbg_arp(void)
daniele 0:d7f2341ab245 162 {
daniele 0:d7f2341ab245 163 struct pico_arp *a;
daniele 0:d7f2341ab245 164 struct pico_tree_node * index;
daniele 0:d7f2341ab245 165
daniele 0:d7f2341ab245 166 pico_tree_foreach(index,&arp_tree) {
daniele 0:d7f2341ab245 167 a = index->keyValue;
daniele 0:d7f2341ab245 168 arp_dbg("ARP to %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr,a->eth.addr[0],a->eth.addr[1],a->eth.addr[2],a->eth.addr[3],a->eth.addr[4],a->eth.addr[5] );
daniele 0:d7f2341ab245 169 }
daniele 0:d7f2341ab245 170 }
daniele 0:d7f2341ab245 171 #endif
daniele 0:d7f2341ab245 172
daniele 0:d7f2341ab245 173 void arp_expire(unsigned long now, void *_stale)
daniele 0:d7f2341ab245 174 {
daniele 0:d7f2341ab245 175 struct pico_arp *stale = (struct pico_arp *) _stale;
daniele 0:d7f2341ab245 176 stale->arp_status = PICO_ARP_STATUS_STALE;
daniele 0:d7f2341ab245 177 arp_dbg("ARP: Setting arp_status to STALE\n");
daniele 0:d7f2341ab245 178 pico_arp_query(stale->dev, &stale->ipv4);
daniele 0:d7f2341ab245 179
daniele 0:d7f2341ab245 180 }
daniele 0:d7f2341ab245 181
daniele 0:d7f2341ab245 182 void pico_arp_add_entry(struct pico_arp *entry)
daniele 0:d7f2341ab245 183 {
daniele 0:d7f2341ab245 184 entry->arp_status = PICO_ARP_STATUS_REACHABLE;
daniele 0:d7f2341ab245 185 entry->timestamp = PICO_TIME();
daniele 0:d7f2341ab245 186
daniele 0:d7f2341ab245 187 pico_tree_insert(&arp_tree,entry);
daniele 0:d7f2341ab245 188 arp_dbg("ARP ## reachable.\n");
daniele 0:d7f2341ab245 189 pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
daniele 0:d7f2341ab245 190 }
daniele 0:d7f2341ab245 191
daniele 0:d7f2341ab245 192 int pico_arp_create_entry(uint8_t* hwaddr, struct pico_ip4 ipv4, struct pico_device* dev)
daniele 0:d7f2341ab245 193 {
daniele 0:d7f2341ab245 194 struct pico_arp* arp = pico_zalloc(sizeof(struct pico_arp));
daniele 0:d7f2341ab245 195 if(!arp){
daniele 0:d7f2341ab245 196 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 197 return -1;
daniele 0:d7f2341ab245 198 }
daniele 0:d7f2341ab245 199 memcpy(arp->eth.addr, hwaddr, 6);
daniele 0:d7f2341ab245 200 arp->ipv4.addr = ipv4.addr;
daniele 0:d7f2341ab245 201 arp->dev = dev;
daniele 0:d7f2341ab245 202
daniele 0:d7f2341ab245 203 pico_arp_add_entry(arp);
daniele 0:d7f2341ab245 204
daniele 0:d7f2341ab245 205 return 0;
daniele 0:d7f2341ab245 206 }
daniele 0:d7f2341ab245 207
daniele 0:d7f2341ab245 208 int pico_arp_receive(struct pico_frame *f)
daniele 0:d7f2341ab245 209 {
daniele 0:d7f2341ab245 210 struct pico_arp_hdr *hdr;
daniele 0:d7f2341ab245 211 struct pico_arp search, *found, *new = NULL;
daniele 0:d7f2341ab245 212 int ret = -1;
daniele 0:d7f2341ab245 213 hdr = (struct pico_arp_hdr *) f->net_hdr;
daniele 0:d7f2341ab245 214
daniele 0:d7f2341ab245 215 if (!hdr)
daniele 0:d7f2341ab245 216 goto end;
daniele 0:d7f2341ab245 217
daniele 0:d7f2341ab245 218
daniele 0:d7f2341ab245 219 /* Populate a new arp entry */
daniele 0:d7f2341ab245 220 search.ipv4.addr = hdr->src.addr;
daniele 0:d7f2341ab245 221 memcpy(search.eth.addr, hdr->s_mac, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 222
daniele 0:d7f2341ab245 223 /* Search for already existing entry */
daniele 0:d7f2341ab245 224
daniele 0:d7f2341ab245 225 found = pico_tree_findKey(&arp_tree,&search);
daniele 0:d7f2341ab245 226 if (!found) {
daniele 0:d7f2341ab245 227 new = pico_zalloc(sizeof(struct pico_arp));
daniele 0:d7f2341ab245 228 if (!new)
daniele 0:d7f2341ab245 229 goto end;
daniele 0:d7f2341ab245 230 new->ipv4.addr = hdr->src.addr;
daniele 0:d7f2341ab245 231 }
daniele 0:d7f2341ab245 232 else if (found->arp_status == PICO_ARP_STATUS_STALE) {
daniele 0:d7f2341ab245 233 /* Replace if stale */
daniele 0:d7f2341ab245 234 new = found;
daniele 0:d7f2341ab245 235
daniele 0:d7f2341ab245 236 pico_tree_delete(&arp_tree,new);
daniele 0:d7f2341ab245 237 }
daniele 0:d7f2341ab245 238
daniele 0:d7f2341ab245 239 ret = 0;
daniele 0:d7f2341ab245 240
daniele 0:d7f2341ab245 241 if (new) {
daniele 0:d7f2341ab245 242 memcpy(new->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 243 new->dev = f->dev;
daniele 0:d7f2341ab245 244 pico_arp_add_entry(new);
daniele 0:d7f2341ab245 245 }
daniele 0:d7f2341ab245 246
daniele 0:d7f2341ab245 247 if (hdr->opcode == PICO_ARP_REQUEST) {
daniele 0:d7f2341ab245 248 struct pico_ip4 me;
daniele 0:d7f2341ab245 249 struct pico_eth_hdr *eh = (struct pico_eth_hdr *)f->datalink_hdr;
daniele 0:d7f2341ab245 250 struct pico_device *link_dev;
daniele 0:d7f2341ab245 251 me.addr = hdr->dst.addr;
daniele 0:d7f2341ab245 252
daniele 0:d7f2341ab245 253 link_dev = pico_ipv4_link_find(&me);
daniele 0:d7f2341ab245 254 if (link_dev != f->dev)
daniele 0:d7f2341ab245 255 goto end;
daniele 0:d7f2341ab245 256
daniele 0:d7f2341ab245 257 hdr->opcode = PICO_ARP_REPLY;
daniele 0:d7f2341ab245 258 memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 259 memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 260 hdr->dst.addr = hdr->src.addr;
daniele 0:d7f2341ab245 261 hdr->src.addr = me.addr;
daniele 0:d7f2341ab245 262
daniele 0:d7f2341ab245 263 /* Prepare eth header for arp reply */
daniele 0:d7f2341ab245 264 memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 265 memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 266 f->start = f->datalink_hdr;
daniele 0:d7f2341ab245 267 f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
daniele 0:d7f2341ab245 268 f->dev->send(f->dev, f->start, f->len);
daniele 0:d7f2341ab245 269 }
daniele 0:d7f2341ab245 270
daniele 0:d7f2341ab245 271 #ifdef DEBUG_ARG
daniele 0:d7f2341ab245 272 dbg_arp();
daniele 0:d7f2341ab245 273 #endif
daniele 0:d7f2341ab245 274
daniele 0:d7f2341ab245 275 end:
daniele 0:d7f2341ab245 276 pico_frame_discard(f);
daniele 0:d7f2341ab245 277 return ret;
daniele 0:d7f2341ab245 278 }
daniele 0:d7f2341ab245 279
daniele 0:d7f2341ab245 280 int pico_arp_query(struct pico_device *dev, struct pico_ip4 *dst)
daniele 0:d7f2341ab245 281 {
daniele 0:d7f2341ab245 282 struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
daniele 0:d7f2341ab245 283 struct pico_eth_hdr *eh;
daniele 0:d7f2341ab245 284 struct pico_arp_hdr *ah;
daniele 0:d7f2341ab245 285 struct pico_ip4 *src;
daniele 0:d7f2341ab245 286 int ret;
daniele 0:d7f2341ab245 287
daniele 0:d7f2341ab245 288 src = pico_ipv4_source_find(dst);
daniele 0:d7f2341ab245 289 if (!src)
daniele 0:d7f2341ab245 290 return -1;
daniele 0:d7f2341ab245 291
daniele 0:d7f2341ab245 292 arp_dbg("QUERY: %08x\n", dst->addr);
daniele 0:d7f2341ab245 293
daniele 0:d7f2341ab245 294 if (!q)
daniele 0:d7f2341ab245 295 return -1;
daniele 0:d7f2341ab245 296 eh = (struct pico_eth_hdr *)q->start;
daniele 0:d7f2341ab245 297 ah = (struct pico_arp_hdr *) (q->start + PICO_SIZE_ETHHDR);
daniele 0:d7f2341ab245 298
daniele 0:d7f2341ab245 299 /* Fill eth header */
daniele 0:d7f2341ab245 300 memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 301 memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 302 eh->proto = PICO_IDETH_ARP;
daniele 0:d7f2341ab245 303
daniele 0:d7f2341ab245 304 /* Fill arp header */
daniele 0:d7f2341ab245 305 ah->htype = PICO_ARP_HTYPE_ETH;
daniele 0:d7f2341ab245 306 ah->ptype = PICO_IDETH_IPV4;
daniele 0:d7f2341ab245 307 ah->hsize = PICO_SIZE_ETH;
daniele 0:d7f2341ab245 308 ah->psize = PICO_SIZE_IP4;
daniele 0:d7f2341ab245 309 ah->opcode = PICO_ARP_REQUEST;
daniele 0:d7f2341ab245 310 memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
daniele 0:d7f2341ab245 311 ah->src.addr = src->addr;
daniele 0:d7f2341ab245 312 ah->dst.addr = dst->addr;
daniele 0:d7f2341ab245 313 arp_dbg("Sending arp query.\n");
daniele 0:d7f2341ab245 314 ret = dev->send(dev, q->start, q->len);
daniele 0:d7f2341ab245 315 pico_frame_discard(q);
daniele 0:d7f2341ab245 316 return ret;
daniele 0:d7f2341ab245 317 }