void handle_arpreq(struct sr_instance *sr, struct sr_arpreq *req) { time_t now = time(NULL); if(difftime(now, req->sent) > 1.0) { /* request timeout */ if(req->times_sent > 5) { struct sr_packet *packets = req->packets; /* iterate through all packets on queue */ while(packets) { uint8_t *reply_packet = 0; sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(packets->buf+sizeof(sr_ethernet_hdr_t)); reply_packet = sr_generate_icmp((sr_ethernet_hdr_t *)packets->buf, ip_hdr, sr_get_interface(sr, packets->iface), 3, 1); /* create ICMP type 3, code 1 (host unreachable) */ /* if ICMP packet fails to generate */ if(reply_packet == 0) { fprintf(stderr, "Error: failed to generate ICMP packet\n"); } /* send ICMP packet to ip of packet in queue */ if(sr_send_packet(sr, reply_packet, sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_t3_hdr_t), (const char*)(packets->iface)) == -1) { fprintf(stderr, "Error: sending packet failed (handle_arpreq)"); } packets = packets->next; free(reply_packet); } sr_arpreq_destroy(&(sr->cache), req); } else { /* generate ARP packet */ struct sr_if *iface = 0; /* if interface is not found */ if((iface = sr_get_interface(sr, req->packets->iface)) == 0) { fprintf(stderr, "Error: interface does not exist (handle_arpreq)"); return; } uint8_t *arp_pkt = sr_new_arpreq_packet(NULL, iface->addr, req->ip, iface->ip); /* create ARP request packet to re-send */ /* send ARP request packet */ if (sr_send_packet(sr, arp_pkt, sizeof(sr_ethernet_hdr_t) + sizeof(sr_arp_hdr_t), (const char*)(iface->name))==-1) { fprintf(stderr, "Error: sending packet failed."); } /* update arpreq fields */ req->times_sent++; req->sent = now; free(arp_pkt); } } }
void sr_handleip(struct sr_instance* sr, uint8_t * packet/* lent */, unsigned int len, char* interface/* lent */) { sr_ip_hdr_t *ip_hdr = 0; struct sr_if *iface = 0; sr_icmp_hdr_t *icmp_hdr = 0; uint8_t *reply_packet = 0; struct sr_rt *rt = 0; uint32_t nexthop_ip, longest_mask = 0; struct sr_arpentry *arp_entry = 0; struct sr_arpreq *arp_req = 0; sr_ethernet_hdr_t *ether_hdr = 0; int matched = 0; /* check if header has the correct size */ if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)) { fprintf(stderr, "Error: invalid IP header length\n"); return; } ip_hdr = (sr_ip_hdr_t*)(packet + sizeof(sr_ethernet_hdr_t)); /* perform ip header checksum */ if (cksum(ip_hdr, ip_hdr->ip_hl) != 0xffff) { fprintf(stderr, "Error: IP checksum failed\n"); return; } /* grab the receiving interface */ if ((iface = sr_get_interface(sr, interface)) == 0) { fprintf(stderr, "Error: interface does not exist (sr_handleip)\n"); return; } /* if the packet is destined to our ip */ if (ip_hdr->ip_dst == htonl(iface->ip)) { /* if it is an ICMP */ if (ip_hdr->ip_p == htons(ip_protocol_icmp)) { /* check if header has the correct size */ if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_hdr_t)) { fprintf(stderr, "Error: invalid ICMP header length\n"); return; } icmp_hdr = (sr_icmp_hdr_t*)(packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); /* if it is an ICMP echo request, send an ICMP echo reply */ if (icmp_hdr->icmp_type == htons(8) && icmp_hdr->icmp_code == htons(0)) { /* perform ICMP header checksum */ if (cksum(icmp_hdr, sizeof(icmp_hdr)) != 0xffff) { fprintf(stderr, "Error: ICMP checksum failed\n"); return; } /* generate an echo reply packet */ if ((reply_packet = sr_generate_icmp((sr_ethernet_hdr_t *)packet, ip_hdr, iface, 0, 0)) == 0) { fprintf(stderr, "Error: failed to generate ICMP echo reply packet\n"); return; } /* send an ICMP echo reply */ if (sr_send_packet(sr, reply_packet, sizeof(reply_packet), (const char*)interface) == -1) { fprintf(stderr, "Error: sending packet failed (sr_handleip)\n"); } free(reply_packet); } } /* if it contains a TCP or UDP payload */ else { /* generate Destination net unreachable (type 3, code 0) reply packet */ if ((reply_packet = sr_generate_icmp((sr_ethernet_hdr_t *)packet, ip_hdr, iface, 3, 3)) == 0) { fprintf(stderr, "Error: failed to generate ICMP packet\n"); return; } /* send an ICMP */ if (sr_send_packet(sr, reply_packet, sizeof(reply_packet), (const char*)interface) == -1) { fprintf(stderr, "Error: sending packet failed (sr_handleip)\n"); } free(reply_packet); } } /* packet not for us, forward it */ else { /* if TTL reaches 0 */ if (ip_hdr->ip_ttl <= htons(1)) { /* generate Time exceeded (type 11, code 0) reply packet */ if ((reply_packet = sr_generate_icmp((sr_ethernet_hdr_t *)packet, ip_hdr, iface, 11, 0)) == 0) { fprintf(stderr, "Error: failed to generate ICMP packet\n"); return; } /* send an ICMP */ if (sr_send_packet(sr, reply_packet, sizeof(reply_packet), (const char*)interface) == -1) { fprintf(stderr, "Error: sending packet failed (sr_handleip)\n"); } free(reply_packet); } /* if packet has enough TTL */ else { /* decrement the TTL by 1 */ ip_hdr->ip_ttl --; /* recompute the packet checksum */ ip_hdr->ip_sum = htons(0); ip_hdr->ip_sum = cksum(ip_hdr, sizeof(sr_ip_hdr_t)); /* Find entry in the routing table with the longest prefix match */ rt = sr->routing_table; while (rt != NULL) { /* update the gateway ip and the longest mask so far */ if ((rt->dest.s_addr & rt->mask.s_addr) == (ntohl(ip_hdr->ip_dst) & rt->mask.s_addr) && rt->mask.s_addr > longest_mask) { nexthop_ip = rt->gw.s_addr; longest_mask = rt->mask.s_addr; matched = 1; } rt = rt->next; } /* if a matching routing table entry was NOT found */ if (matched == 0) { /* generate Destination net unreachable (type 3, code 0) reply packet */ if ((reply_packet = sr_generate_icmp((sr_ethernet_hdr_t *)packet, ip_hdr, iface, 3, 0)) == 0) { fprintf(stderr, "Error: failed to generate ICMP packet\n"); return; } /* send an ICMP */ if (sr_send_packet(sr, reply_packet, sizeof(reply_packet), (const char*)interface) == -1) { fprintf(stderr, "Error: sending packet failed (sr_handleip)\n"); } free(reply_packet); } /* if a matching routing table entry was found */ else { /* if the next hop is 0.0.0.0 */ if(nexthop_ip == 0) { nexthop_ip = ip_hdr->ip_dst; } /* set the source MAC of ethernet header */ ether_hdr = (sr_ethernet_hdr_t*)packet; memcpy(ether_hdr->ether_shost, iface->addr, ETHER_ADDR_LEN); /* if the next-hop IP CANNOT be found in ARP cache */ if ((arp_entry = sr_arpcache_lookup(&(sr->cache), htonl(nexthop_ip))) == NULL) { /* send an ARP request */ arp_req = sr_arpcache_queuereq(&(sr->cache), nexthop_ip, packet, len, iface); handle_arpreq(sr, arp_req); } /* if the next-hop IP can be found in ARP cache */ else { /* set the destination MAC of ethernet header */ memcpy(ether_hdr->ether_dhost, arp_entry->mac, ETHER_ADDR_LEN); /* send the packet */ if (sr_send_packet(sr, packet, sizeof(packet), (const char*)interface) == -1) { fprintf(stderr, "Error: sending packet failed (sr_handlearp)\n"); } free(arp_entry); } } } } }