Daniele Lacamera / PicoTCP-Experimental_CDC_ECM_Branch

Fork of PicoTCP by Daniele Lacamera

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pico_arp.c Source File

pico_arp.c

00001 /*********************************************************************
00002 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
00003 See LICENSE and COPYING for usage.
00004 
00005 .
00006 
00007 Authors: Daniele Lacamera
00008 *********************************************************************/
00009 
00010 
00011 #include "pico_config.h"
00012 #include "pico_arp.h"
00013 #include "pico_tree.h"
00014 #include "pico_ipv4.h"
00015 #include "pico_device.h"
00016 #include "pico_stack.h"
00017 
00018 const uint8_t PICO_ETHADDR_ALL[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
00019 #define PICO_ARP_TIMEOUT 600000
00020 #define PICO_ARP_RETRY 300
00021 
00022 #ifdef DEBUG_ARP
00023     #define arp_dbg dbg
00024 #else
00025     #define arp_dbg(...) do{}while(0)
00026 #endif
00027 
00028 static struct pico_queue pending;
00029 static int pending_timer_on = 0;
00030 
00031 void check_pending(unsigned long now, void *_unused)
00032 {
00033   struct pico_frame *f = pico_dequeue(&pending);
00034   if (!f) {
00035     pending_timer_on = 0;
00036     return;
00037   }
00038   if(pico_ethernet_send(f) > 0)
00039     pico_frame_discard(f);
00040   pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
00041 }
00042 
00043 
00044 struct
00045 __attribute__ ((__packed__)) 
00046 pico_arp_hdr
00047 {
00048   uint16_t htype;
00049   uint16_t ptype;
00050   uint8_t hsize;
00051   uint8_t psize;
00052   uint16_t opcode;
00053   uint8_t s_mac[PICO_SIZE_ETH];
00054   struct pico_ip4 src;
00055   uint8_t d_mac[PICO_SIZE_ETH];
00056   struct pico_ip4 dst;
00057 };
00058 
00059 
00060 #define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
00061 
00062 /* Arp Entries for the tables. */
00063 struct pico_arp {
00064 /* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure, 
00065  * due to in-place casting!!! */
00066   struct pico_eth eth;
00067   struct pico_ip4 ipv4;
00068   int    arp_status;
00069   uint32_t timestamp;
00070   struct pico_device *dev;
00071 };
00072 
00073 
00074 
00075 /*****************/
00076 /**  ARP TREE **/
00077 /*****************/
00078 
00079 /* Routing destination */
00080 
00081 static int arp_compare(void * ka, void * kb)
00082 {
00083     struct pico_arp *a = ka, *b = kb;
00084   if (a->ipv4.addr < b->ipv4.addr)
00085     return -1;
00086   else if (a->ipv4.addr > b->ipv4.addr)
00087     return 1;
00088   return 0;
00089 }
00090 
00091 PICO_TREE_DECLARE(arp_tree, arp_compare);
00092 
00093 /*********************/
00094 /**  END ARP TREE **/
00095 /*********************/
00096 
00097 struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
00098 {
00099   struct pico_arp search, *found;
00100   search.ipv4.addr = dst->addr;
00101   found = pico_tree_findKey(&arp_tree,&search);
00102   if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
00103     return &found->eth;
00104   return NULL;
00105 }
00106 
00107 struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
00108 {
00109   struct pico_arp* search;
00110   struct pico_tree_node * index;
00111   pico_tree_foreach(index,&arp_tree){
00112       search = index->keyValue;
00113     if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
00114       return &search->ipv4;
00115   }
00116   return NULL;
00117 }
00118 
00119 struct pico_eth *pico_arp_get(struct pico_frame *f) {
00120   struct pico_eth *a4;
00121   struct pico_ip4 gateway;
00122   struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
00123   struct pico_ipv4_link *l;
00124 
00125   l = pico_ipv4_link_get(&hdr->dst);
00126   if(l){
00127     //address belongs to ourself
00128     return &l->dev->eth->mac;
00129   }
00130 
00131   gateway = pico_ipv4_route_get_gateway(&hdr->dst);
00132   /* check if dst is local (gateway = 0), or if to use gateway */
00133   if (gateway.addr != 0)
00134     a4 = pico_arp_lookup(&gateway);          /* check if gateway ip mac in cache */
00135   else
00136     a4 = pico_arp_lookup(&hdr->dst);         /* check if local ip mac in cache */
00137   if (!a4) {
00138      if (++f->failure_count < 4) {
00139        dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
00140        /* check if dst is local (gateway = 0), or if to use gateway */
00141        if (gateway.addr != 0)
00142          pico_arp_query(f->dev, &gateway);  /* arp to gateway */
00143        else
00144          pico_arp_query(f->dev, &hdr->dst); /* arp to dst */
00145 
00146        pico_enqueue(&pending, f);
00147        if (!pending_timer_on) {
00148         pending_timer_on++;
00149         pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
00150        }
00151      } else {
00152       dbg("ARP: Destination Unreachable\n");
00153       pico_notify_dest_unreachable(f);
00154       pico_frame_discard(f);
00155     }
00156   }
00157   return a4;
00158 }
00159 
00160 #ifdef DEBUG_ARP
00161 void dbg_arp(void)
00162 {
00163   struct pico_arp *a;
00164   struct pico_tree_node * index;
00165 
00166   pico_tree_foreach(index,&arp_tree) {
00167       a = index->keyValue;
00168     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] );
00169   }
00170 }
00171 #endif
00172 
00173 void arp_expire(unsigned long now, void *_stale)
00174 {
00175   struct pico_arp *stale = (struct pico_arp *) _stale;
00176   stale->arp_status = PICO_ARP_STATUS_STALE;
00177   arp_dbg("ARP: Setting arp_status to STALE\n");
00178   pico_arp_query(stale->dev, &stale->ipv4);
00179 
00180 }
00181 
00182 void pico_arp_add_entry(struct pico_arp *entry)
00183 {
00184     entry->arp_status = PICO_ARP_STATUS_REACHABLE;
00185     entry->timestamp  = PICO_TIME();
00186 
00187     pico_tree_insert(&arp_tree,entry);
00188     arp_dbg("ARP ## reachable.\n");
00189     pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
00190 }
00191 
00192 int pico_arp_create_entry(uint8_t* hwaddr, struct pico_ip4 ipv4, struct pico_device* dev)
00193 {
00194     struct pico_arp* arp = pico_zalloc(sizeof(struct pico_arp));
00195     if(!arp){
00196         pico_err = PICO_ERR_ENOMEM;
00197         return -1;
00198     }
00199     memcpy(arp->eth.addr, hwaddr, 6);
00200     arp->ipv4.addr = ipv4.addr;
00201     arp->dev = dev;
00202 
00203     pico_arp_add_entry(arp);
00204 
00205     return 0;
00206 }
00207 
00208 int pico_arp_receive(struct pico_frame *f)
00209 {
00210   struct pico_arp_hdr *hdr;
00211   struct pico_arp search, *found, *new = NULL;
00212   int ret = -1;
00213   hdr = (struct pico_arp_hdr *) f->net_hdr;
00214 
00215   if (!hdr)
00216     goto end;
00217 
00218 
00219   /* Populate a new arp entry */
00220   search.ipv4.addr = hdr->src.addr;
00221   memcpy(search.eth.addr, hdr->s_mac, PICO_SIZE_ETH);
00222 
00223   /* Search for already existing entry */
00224 
00225   found = pico_tree_findKey(&arp_tree,&search);
00226   if (!found) {
00227     new = pico_zalloc(sizeof(struct pico_arp));
00228     if (!new)
00229       goto end;
00230     new->ipv4.addr = hdr->src.addr;
00231   }
00232   else if (found->arp_status == PICO_ARP_STATUS_STALE) {
00233     /* Replace if stale */
00234     new = found;
00235 
00236     pico_tree_delete(&arp_tree,new);
00237   }
00238 
00239   ret = 0;
00240 
00241   if (new) {
00242     memcpy(new->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
00243     new->dev = f->dev;
00244     pico_arp_add_entry(new);
00245   }
00246 
00247   if (hdr->opcode == PICO_ARP_REQUEST) {
00248     struct pico_ip4 me;
00249     struct pico_eth_hdr *eh = (struct pico_eth_hdr *)f->datalink_hdr;
00250     struct pico_device *link_dev;
00251     me.addr = hdr->dst.addr;
00252 
00253     link_dev = pico_ipv4_link_find(&me);
00254     if (link_dev != f->dev)
00255       goto end;
00256 
00257     hdr->opcode = PICO_ARP_REPLY;
00258     memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
00259     memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
00260     hdr->dst.addr = hdr->src.addr;
00261     hdr->src.addr = me.addr;
00262 
00263     /* Prepare eth header for arp reply */
00264     memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
00265     memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
00266     f->start = f->datalink_hdr;
00267     f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
00268     f->dev->send(f->dev, f->start, f->len);
00269   }
00270 
00271 #ifdef DEBUG_ARG
00272   dbg_arp();
00273 #endif
00274 
00275 end:
00276   pico_frame_discard(f);
00277   return ret;
00278 }
00279 
00280 int pico_arp_query(struct pico_device *dev, struct pico_ip4 *dst)
00281 {
00282   struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
00283   struct pico_eth_hdr *eh;
00284   struct pico_arp_hdr *ah;
00285   struct pico_ip4 *src;
00286   int ret;
00287 
00288   src = pico_ipv4_source_find(dst);
00289   if (!src)
00290     return -1;
00291 
00292   arp_dbg("QUERY: %08x\n", dst->addr);
00293 
00294   if (!q)
00295     return -1;
00296   eh = (struct pico_eth_hdr *)q->start;
00297   ah = (struct pico_arp_hdr *) (q->start + PICO_SIZE_ETHHDR);
00298 
00299   /* Fill eth header */
00300   memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
00301   memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
00302   eh->proto = PICO_IDETH_ARP;
00303 
00304   /* Fill arp header */
00305   ah->htype  = PICO_ARP_HTYPE_ETH;
00306   ah->ptype  = PICO_IDETH_IPV4;
00307   ah->hsize  = PICO_SIZE_ETH;
00308   ah->psize  = PICO_SIZE_IP4;
00309   ah->opcode = PICO_ARP_REQUEST;
00310   memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
00311   ah->src.addr = src->addr;
00312   ah->dst.addr = dst->addr;
00313   arp_dbg("Sending arp query.\n");
00314   ret = dev->send(dev, q->start, q->len);
00315   pico_frame_discard(q);
00316   return ret;
00317 }