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

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

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-2015 Altran Intelligent Systems. 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 extern const uint8_t PICO_ETHADDR_ALL[6];
00019 #define PICO_ARP_TIMEOUT 600000llu
00020 #define PICO_ARP_RETRY 300lu
00021 #define PICO_ARP_MAX_PENDING 5
00022 
00023 #ifdef DEBUG_ARP
00024     #define arp_dbg dbg
00025 #else
00026     #define arp_dbg(...) do {} while(0)
00027 #endif
00028 
00029 static int max_arp_reqs = PICO_ARP_MAX_RATE;
00030 static struct pico_frame *frames_queued[PICO_ARP_MAX_PENDING] = { 0 };
00031 
00032 static void pico_arp_queued_trigger(void)
00033 {
00034     int i;
00035     struct pico_frame *f;
00036     for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
00037     {
00038         f = frames_queued[i];
00039         if (f) {
00040             if (!pico_ethernet_send(f))
00041             {
00042                 pico_frame_discard(f);
00043                 frames_queued[i] = NULL;
00044             }
00045         }
00046     }
00047 }
00048 
00049 static void update_max_arp_reqs(pico_time now, void *unused)
00050 {
00051     IGNORE_PARAMETER(now);
00052     IGNORE_PARAMETER(unused);
00053     if (max_arp_reqs < PICO_ARP_MAX_RATE)
00054         max_arp_reqs++;
00055 
00056     pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
00057 }
00058 
00059 void pico_arp_init(void)
00060 {
00061     pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
00062 }
00063 
00064 PACKED_STRUCT_DEF pico_arp_hdr
00065 {
00066     uint16_t htype;
00067     uint16_t ptype;
00068     uint8_t hsize;
00069     uint8_t psize;
00070     uint16_t opcode;
00071     uint8_t s_mac[PICO_SIZE_ETH];
00072     struct pico_ip4 src;
00073     uint8_t d_mac[PICO_SIZE_ETH];
00074     struct pico_ip4 dst;
00075 };
00076 
00077 
00078 
00079 /* Callback handler for ip conflict service (e.g. IPv4 SLAAC)
00080  *  Whenever the IP address registered here is seen in the network,
00081  *  the callback is awaken to take countermeasures against IP collisions.
00082  *
00083  */
00084 
00085 struct arp_service_ipconflict {
00086     struct pico_eth mac;
00087     struct pico_ip4 ip;
00088     void (*conflict)(void);
00089 };
00090 
00091 static struct arp_service_ipconflict conflict_ipv4;
00092 
00093 
00094 
00095 #define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
00096 
00097 /* Arp Entries for the tables. */
00098 struct pico_arp {
00099 /* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure,
00100  * due to in-place casting!!! */
00101     struct pico_eth eth;
00102     struct pico_ip4 ipv4;
00103     int arp_status;
00104     pico_time timestamp;
00105     struct pico_device *dev;
00106     struct pico_timer *timer;
00107 };
00108 
00109 
00110 
00111 /*****************/
00112 /**  ARP TREE **/
00113 /*****************/
00114 
00115 /* Routing destination */
00116 
00117 static int arp_compare(void *ka, void *kb)
00118 {
00119     struct pico_arp *a = ka, *b = kb;
00120     return pico_ipv4_compare(&a->ipv4, &b->ipv4);
00121 }
00122 
00123 PICO_TREE_DECLARE(arp_tree, arp_compare);
00124 
00125 /*********************/
00126 /**  END ARP TREE **/
00127 /*********************/
00128 
00129 struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
00130 {
00131     struct pico_arp search, *found;
00132     search.ipv4.addr = dst->addr;
00133     found = pico_tree_findKey(&arp_tree, &search);
00134     if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
00135         return &found->eth;
00136 
00137     return NULL;
00138 }
00139 
00140 struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
00141 {
00142     struct pico_arp*search;
00143     struct pico_tree_node *index;
00144     pico_tree_foreach(index, &arp_tree){
00145         search = index->keyValue;
00146         if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
00147             return &search->ipv4;
00148     }
00149     return NULL;
00150 }
00151 
00152 static void pico_arp_unreachable(struct pico_ip4 *a)
00153 {
00154     int i;
00155     struct pico_frame *f;
00156     struct pico_ipv4_hdr *hdr;
00157     struct pico_ip4 dst;
00158     for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
00159     {
00160         f = frames_queued[i];
00161         if (f) {
00162             hdr = (struct pico_ipv4_hdr *) f->net_hdr;
00163             dst = pico_ipv4_route_get_gateway(&hdr->dst);
00164             if (!dst.addr)
00165                 dst.addr = hdr->dst.addr;
00166 
00167             if (dst.addr ==  a->addr) {
00168                 if (!pico_source_is_local(f)) {
00169                     pico_notify_dest_unreachable(f);
00170                 }
00171 
00172                 pico_frame_discard(f);
00173                 frames_queued[i] = NULL;
00174             }
00175         }
00176     }
00177 }
00178 
00179 static void pico_arp_retry(struct pico_frame *f, struct pico_ip4 *where)
00180 {
00181     if (++f->failure_count < 4) {
00182         arp_dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
00183         /* check if dst is local (gateway = 0), or if to use gateway */
00184         pico_arp_request(f->dev, where, PICO_ARP_QUERY);
00185     } else {
00186         pico_arp_unreachable(where);
00187     }
00188 }
00189 
00190 struct pico_eth *pico_arp_get(struct pico_frame *f)
00191 {
00192     struct pico_eth *a4;
00193     struct pico_ip4 gateway;
00194     struct pico_ip4 *where;
00195     struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
00196     struct pico_ipv4_link *l;
00197     if (!hdr)
00198         return NULL;
00199 
00200     l = pico_ipv4_link_get(&hdr->dst);
00201     if(l) {
00202         /* address belongs to ourself */
00203         return &l->dev->eth->mac;
00204     }
00205 
00206     gateway = pico_ipv4_route_get_gateway(&hdr->dst);
00207     /* check if dst is local (gateway = 0), or if to use gateway */
00208     if (gateway.addr != 0)
00209         where = &gateway;
00210     else
00211         where = &hdr->dst;
00212 
00213     a4 = pico_arp_lookup(where);      /* check if dst ip mac in cache */
00214 
00215     if (!a4)
00216         pico_arp_retry(f, where);
00217 
00218     return a4;
00219 }
00220 
00221 
00222 void pico_arp_postpone(struct pico_frame *f)
00223 {
00224     int i;
00225     for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
00226     {
00227         if (!frames_queued[i]) {
00228             frames_queued[i] = pico_frame_copy(f);
00229             return;
00230         }
00231     }
00232     /* Not possible to enqueue: caller will discard packet */
00233 }
00234 
00235 
00236 #ifdef DEBUG_ARP
00237 void dbg_arp(void)
00238 {
00239     struct pico_arp *a;
00240     struct pico_tree_node *index;
00241 
00242     pico_tree_foreach(index, &arp_tree) {
00243         a = index->keyValue;
00244         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] );
00245     }
00246 }
00247 #endif
00248 
00249 static void arp_expire(pico_time now, void *_stale)
00250 {
00251     struct pico_arp *stale = (struct pico_arp *) _stale;
00252     if (now >= (stale->timestamp + PICO_ARP_TIMEOUT)) {
00253         stale->arp_status = PICO_ARP_STATUS_STALE;
00254         arp_dbg("ARP: Setting arp_status to STALE\n");
00255         pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY);
00256     } else {
00257         /* Timer must be rescheduled, ARP entry has been renewed lately.
00258          * No action required to refresh the entry, will check on the next timeout */
00259         pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale);
00260     }
00261 }
00262 
00263 static void pico_arp_add_entry(struct pico_arp *entry)
00264 {
00265     entry->arp_status = PICO_ARP_STATUS_REACHABLE;
00266     entry->timestamp  = PICO_TIME();
00267 
00268     pico_tree_insert(&arp_tree, entry);
00269     arp_dbg("ARP ## reachable.\n");
00270     pico_arp_queued_trigger();
00271     pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
00272 }
00273 
00274 int pico_arp_create_entry(uint8_t *hwaddr, struct pico_ip4 ipv4, struct pico_device *dev)
00275 {
00276     struct pico_arp*arp = PICO_ZALLOC(sizeof(struct pico_arp));
00277     if(!arp) {
00278         pico_err = PICO_ERR_ENOMEM;
00279         return -1;
00280     }
00281 
00282     memcpy(arp->eth.addr, hwaddr, 6);
00283     arp->ipv4.addr = ipv4.addr;
00284     arp->dev = dev;
00285 
00286     pico_arp_add_entry(arp);
00287 
00288     return 0;
00289 }
00290 
00291 static void pico_arp_check_conflict(struct pico_arp_hdr *hdr)
00292 {
00293 
00294     if ((conflict_ipv4.conflict) &&
00295         ((conflict_ipv4.ip.addr == hdr->src.addr) &&
00296          (memcmp(hdr->s_mac, conflict_ipv4.mac.addr, PICO_SIZE_ETH) != 0)))
00297         conflict_ipv4.conflict();
00298 }
00299 
00300 static struct pico_arp *pico_arp_lookup_entry(struct pico_frame *f)
00301 {
00302     struct pico_arp search;
00303     struct pico_arp *found = NULL;
00304     struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
00305     /* Populate a new arp entry */
00306     search.ipv4.addr = hdr->src.addr;
00307 
00308     /* Search for already existing entry */
00309     found = pico_tree_findKey(&arp_tree, &search);
00310     if (found) {
00311         if (found->arp_status == PICO_ARP_STATUS_STALE) {
00312             /* Replace if stale */
00313             pico_tree_delete(&arp_tree, found);
00314             pico_arp_add_entry(found);
00315         } else {
00316             /* Update mac address */
00317             memcpy(found->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
00318             arp_dbg("ARP entry updated!\n");
00319 
00320             /* Refresh timestamp, this will force a reschedule on the next timeout*/
00321             found->timestamp = PICO_TIME();
00322         }
00323     }
00324 
00325     return found;
00326 }
00327 
00328 
00329 static int pico_arp_check_incoming_hdr_type(struct pico_arp_hdr *h)
00330 {
00331     /* Check the hardware type and protocol */
00332     if ((h->htype != PICO_ARP_HTYPE_ETH) || (h->ptype != PICO_IDETH_IPV4))
00333         return -1;
00334 
00335     return 0;
00336 }
00337 
00338 static int pico_arp_check_incoming_hdr(struct pico_frame *f, struct pico_ip4 *dst_addr)
00339 {
00340     struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
00341     if (!hdr)
00342         return -1;
00343 
00344     dst_addr->addr = hdr->dst.addr;
00345     if (pico_arp_check_incoming_hdr_type(hdr) < 0)
00346         return -1;
00347 
00348     /* The source mac address must not be a multicast or broadcast address */
00349     if (hdr->s_mac[0] & 0x01)
00350         return -1;
00351 
00352     return 0;
00353 }
00354 
00355 static void pico_arp_reply_on_request(struct pico_frame *f, struct pico_ip4 me)
00356 {
00357     struct pico_arp_hdr *hdr;
00358     struct pico_eth_hdr *eh;
00359 
00360     hdr = (struct pico_arp_hdr *) f->net_hdr;
00361     eh = (struct pico_eth_hdr *)f->datalink_hdr;
00362     if (hdr->opcode != PICO_ARP_REQUEST)
00363         return;
00364 
00365     hdr->opcode = PICO_ARP_REPLY;
00366     memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
00367     memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
00368     hdr->dst.addr = hdr->src.addr;
00369     hdr->src.addr = me.addr;
00370 
00371     /* Prepare eth header for arp reply */
00372     memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
00373     memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
00374     f->start = f->datalink_hdr;
00375     f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
00376     f->dev->send(f->dev, f->start, (int)f->len);
00377 }
00378 
00379 static int pico_arp_check_flooding(struct pico_frame *f, struct pico_ip4 me)
00380 {
00381     struct pico_device *link_dev;
00382     struct pico_arp_hdr *hdr;
00383     hdr = (struct pico_arp_hdr *) f->net_hdr;
00384 
00385     /* Prevent ARP flooding */
00386     link_dev = pico_ipv4_link_find(&me);
00387     if ((link_dev == f->dev) && (hdr->opcode == PICO_ARP_REQUEST)) {
00388         if (max_arp_reqs == 0)
00389             return -1;
00390         else
00391             max_arp_reqs--;
00392     }
00393 
00394     /* Check if we are the target IP address */
00395     if (link_dev != f->dev)
00396         return -1;
00397 
00398     return 0;
00399 }
00400 
00401 static int pico_arp_process_in(struct pico_frame *f, struct pico_arp_hdr *hdr, struct pico_arp *found)
00402 {
00403     struct pico_ip4 me;
00404     if (pico_arp_check_incoming_hdr(f, &me) < 0) {
00405         pico_frame_discard(f);
00406         return -1;
00407     }
00408 
00409     if (pico_arp_check_flooding(f, me) < 0) {
00410         pico_frame_discard(f);
00411         return -1;
00412     }
00413 
00414     /* If no existing entry was found, create a new entry, or fail trying. */
00415     if ((!found) && (pico_arp_create_entry(hdr->s_mac, hdr->src, f->dev) < 0)) {
00416         pico_frame_discard(f);
00417         return -1;
00418     }
00419 
00420     /* If the packet is a request, send a reply */
00421     pico_arp_reply_on_request(f, me);
00422 
00423 #ifdef DEBUG_ARP
00424     dbg_arp();
00425 #endif
00426     pico_frame_discard(f);
00427     return 0;
00428 }
00429 
00430 int pico_arp_receive(struct pico_frame *f)
00431 {
00432     struct pico_arp_hdr *hdr;
00433     struct pico_arp *found = NULL;
00434 
00435     hdr = (struct pico_arp_hdr *) f->net_hdr;
00436     if (!hdr)
00437         return -1;
00438 
00439     pico_arp_check_conflict(hdr);
00440     found = pico_arp_lookup_entry(f);
00441     return pico_arp_process_in(f, hdr, found);
00442 
00443 }
00444 
00445 static int32_t pico_arp_request_xmit(struct pico_device *dev, struct pico_frame *f, struct pico_ip4 *src, struct pico_ip4 *dst, uint8_t type)
00446 {
00447     struct pico_arp_hdr *ah = (struct pico_arp_hdr *) (f->start + PICO_SIZE_ETHHDR);
00448     int ret;
00449 
00450     /* Fill arp header */
00451     ah->htype  = PICO_ARP_HTYPE_ETH;
00452     ah->ptype  = PICO_IDETH_IPV4;
00453     ah->hsize  = PICO_SIZE_ETH;
00454     ah->psize  = PICO_SIZE_IP4;
00455     ah->opcode = PICO_ARP_REQUEST;
00456     memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
00457 
00458     switch (type) {
00459     case PICO_ARP_ANNOUNCE:
00460         ah->src.addr = dst->addr;
00461         ah->dst.addr = dst->addr;
00462         break;
00463     case PICO_ARP_PROBE:
00464         ah->src.addr = 0;
00465         ah->dst.addr = dst->addr;
00466         break;
00467     case PICO_ARP_QUERY:
00468         ah->src.addr = src->addr;
00469         ah->dst.addr = dst->addr;
00470         break;
00471     default:
00472         pico_frame_discard(f);
00473         return -1;
00474     }
00475     arp_dbg("Sending arp request.\n");
00476     ret = dev->send(dev, f->start, (int) f->len);
00477     pico_frame_discard(f);
00478     return ret;
00479 }
00480 
00481 int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type)
00482 {
00483     struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
00484     struct pico_eth_hdr *eh;
00485     struct pico_ip4 *src = NULL;
00486 
00487     if (!q)
00488         return -1;
00489 
00490     if (type == PICO_ARP_QUERY)
00491     {
00492         src = pico_ipv4_source_find(dst);
00493         if (!src) {
00494             pico_frame_discard(q);
00495             return -1;
00496         }
00497     }
00498 
00499     arp_dbg("QUERY: %08x\n", dst->addr);
00500 
00501     eh = (struct pico_eth_hdr *)q->start;
00502 
00503     /* Fill eth header */
00504     memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
00505     memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
00506     eh->proto = PICO_IDETH_ARP;
00507 
00508     return pico_arp_request_xmit(dev, q, src, dst, type);
00509 }
00510 
00511 int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen)
00512 {
00513     struct pico_arp*search;
00514     struct pico_tree_node *index;
00515     int i = 0;
00516     pico_tree_foreach(index, &arp_tree){
00517         search = index->keyValue;
00518         if (search->dev == dev) {
00519             neighbors[i++].addr = search->ipv4.addr;
00520             if (i >= maxlen)
00521                 return i;
00522         }
00523     }
00524     return i;
00525 }
00526 
00527 void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(void))
00528 {
00529     conflict_ipv4.conflict = cb;
00530     conflict_ipv4.ip.addr = ip->addr;
00531     if (mac != NULL)
00532         memcpy(conflict_ipv4.mac.addr, mac, 6);
00533 }
00534