int arp_table_get_entry(struct sr_instance * sr, uint32_t ip, addr_mac_t * addr){ rmutex_lock(&sr->arptable_lock); time_t now = time(NULL); for(int i = 0; i < ARP_TABLE_SIZE; i++) { if (sr->arptable[i].ipaddr == ip && sr->arptable[i].expiry > now) { *addr = sr->arptable[i].ethaddr; rmutex_unlock(&sr->arptable_lock); return TRUE; } } rmutex_unlock(&sr->arptable_lock); return FALSE; }
int arp_table_set_entry(struct sr_instance * sr, uint32_t ip, addr_mac_t macaddr) { int arp_LRU = -1; int cache_found = 0; int now = time(NULL); rmutex_lock(&sr->arptable_lock); for(int i = 0; i < ARP_TABLE_SIZE; i++) { // find space for this entry in the arp cache if (sr->arptable[i].ipaddr == ip || sr->arptable[i].expiry < now || sr->arptable[i].expiry == 0) { sr->arptable[i].ipaddr = ip; sr->arptable[i].expiry = now + ARP_CACHE_RESIDENCY_TIME; memcpy(&sr->arptable[i].ethaddr,&macaddr,ETHER_ADDR_LEN); cache_found = 1; break; } // in the case that we don't find an empty/stale // arp cache entry, find least recently used and eject that one. if( arp_LRU < 0 || sr->arptable[i].expiry < sr->arptable[arp_LRU].expiry) arp_LRU = i; } if (!cache_found) { sr->arptable[arp_LRU].ipaddr = ip; // default arp cache residency time is 20 minutes sr->arptable[arp_LRU].expiry = now + 60*20; memcpy(&sr->arptable[arp_LRU].ethaddr,&macaddr,ETHER_ADDR_LEN); } rmutex_unlock(&sr->arptable_lock); //TODO optional: signal the pending packet queue to check to see if //it got info it needed to send a packet return TRUE; }
void * arp_queue_caretaker(void * router){ struct sr_instance * sr = router; while(TRUE) { // walk the packet queue, send and clear any that are sendable rmutex_lock(&sr->arp_queue_lock); for(int i = 0; i < OUTSTANDING_ARP_LIMIT; i++) { if (sr->pq[i].payload) { addr_mac_t result; if (arp_table_get_entry(sr,sr->pq[i].ip,&result)) { Debug("found destination in arp cache, sending packet\n"); memcpy(sr->pq[i].payload,&result,ETHER_ADDR_LEN); sr_send_packet(sr,sr->pq[i].payload,sr->pq[i].payload_len,sr->pq[i].iface); free(sr->pq[i].payload); sr->pq[i].payload = NULL; continue; } // last chance failed, drop this packet else if (sr->pq[i].expiry < time(NULL)) { // grab the pointer so we can send the icmp error, // but free up the outgoing IP packet cache entry uint8_t * buf = sr->pq[i].payload; sr->pq[i].payload = NULL; Debug("sending icmp host unreach on arp timeout\n"); // send ICMP HOST UNREACH back to sender to inform // them that we cannot route this packet uint32_t dst = ((struct ip *)(buf + sizeof(struct sr_ethernet_hdr)))->ip_src.s_addr; icmp_send(sr,dst,0,buf + sizeof(struct sr_ethernet_hdr),sr->pq[i].payload_len - sizeof(struct sr_ethernet_hdr),ICMP_TYPE_DEST_UNREACH,ICMP_CODE_HOST_UNREACH,0,0); free(buf); } } } // walk the pending arp request queue, sending or deleting entries // as necessary for(int i = 0; i < OUTSTANDING_ARP_LIMIT; i++) { // clear entries that responses have been found for addr_mac_t ret; if (sr->aq[i].ip && arp_table_get_entry(sr,sr->aq[i].ip,&ret)) { sr->aq[i].ip = 0; continue; } if (sr->aq[i].ip != 0 && sr->aq[i].expiry < time(NULL)) { if(sr->aq[i].req_remaining-- < 1) { // done trying this one, fail sr->aq[i].ip = 0; continue; } // reset the time remaining sr->aq[i].expiry = time(NULL) + ARP_TIMEOUT; // send an ARP request for this packet int len = sizeof(struct sr_ethernet_hdr) + sizeof(struct sr_arphdr); uint8_t * buf = malloc(len); memset(buf,0,len); Debug("crafting arp packet from %s\n",sr->aq[i].iface); addr_mac_t saddr = sr_get_interface(sr,sr->aq[i].iface)->addr; uint32_t src_ip = sr_get_interface(sr,sr->aq[i].iface)->ip; struct sr_ethernet_hdr * eh = (struct sr_ethernet_hdr *)buf; struct sr_arphdr * ah = (struct sr_arphdr *) (buf + sizeof(struct sr_ethernet_hdr)); // set ethernet fields eh->ether_shost = saddr; memset(&eh->ether_dhost,0xFF,ETHER_ADDR_LEN); eh->ether_type = htons(ETHERTYPE_ARP); // set arp fields ah->ar_hrd = htons(ARPHDR_ETHER); ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = 6; ah->ar_pln = 4; ah->ar_op = htons(ARP_REQUEST); ah->ar_sha = saddr; ah->ar_sip = src_ip; ah->ar_tip = sr->aq[i].ip; memset(&ah->ar_tha,0,ETHER_ADDR_LEN); Debug("sr_send_packet sending arp request out interface %s with length %d\n",sr->aq[i].iface,len); sr_send_packet(sr,buf,len,sr->aq[i].iface); free(buf); } } rmutex_unlock(&sr->arp_queue_lock); sleep(1); } return NULL; }
void queue_ethernet_wait_arp(struct sr_instance * sr, struct sr_rt * rti, struct sr_if * intf, uint16_t type, uint8_t* payload, unsigned payload_len ) { // find space on the pq for this outgoing packet uint8_t * buf; unsigned len = ETH_HEADER_LEN + payload_len; buf = malloc(len); rmutex_lock(&sr->arp_queue_lock); /* set the from address field */ memcpy( buf+ETHER_ADDR_LEN, &intf->addr, ETHER_ADDR_LEN ); /* set the type and payload fields */ memcpy( buf+ETHER_ADDR_LEN*2, &type, sizeof(type) ); memcpy( buf+ETH_HEADER_LEN, payload, payload_len ); for(int i = 0; i < OUTSTANDING_ARP_LIMIT ; i++) { // space exists to queue the packet if (sr->pq[i].payload == 0) { // ptr to lowest empty arptable entry struct arp_response_queue_entry * ae = NULL; // walk outstanding arp data structure to find room for( int j = 0; j < OUTSTANDING_ARP_LIMIT; j++) { if(sr->aq[j].ip == rti->gw.s_addr) { time_t now = time(NULL); Debug("arp request already queued for this IP\n"); // queue the packet and return sr->pq[i].payload = buf; sr->pq[i].expiry = now + PACKET_TIMEOUT; sr->pq[i].ip = rti->gw.s_addr; sr->pq[i].iface = intf->name; sr->pq[i].payload_len = len; rmutex_unlock(&sr->arp_queue_lock); return; } else if (sr->aq[j].ip == 0) ae = &sr->aq[j]; } if (!ae) { Debug("pending arp request queue full, dropping packet\n"); rmutex_unlock(&sr->arp_queue_lock); return; } // expiry 0 - arp sent next wakeup Debug("Queueing ARP packet to go out %s\n",intf->name); ae->expiry = 0; ae->req_remaining = ARP_ATTEMPTS; ae->ip = rti->gw.s_addr; ae->iface = intf->name; sr->pq[i].payload = buf; sr->pq[i].payload_len = len; sr->pq[i].expiry = time(NULL) + PACKET_TIMEOUT; sr->pq[i].ip = rti->gw.s_addr; sr->pq[i].iface = intf->name; rmutex_unlock(&sr->arp_queue_lock); return ; } } Debug("arp packet queue full, dropping packet\n"); rmutex_unlock(&sr->arp_queue_lock); }
void gnrc_netif_release(gnrc_netif_t *netif) { if (netif && (netif->ops)) { rmutex_unlock(&netif->mutex); } }