void sr_forward_ip(struct sr_instance *sr, uint8_t *packet, unsigned int len) { assert(sr); assert(packet); sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)); ip_hdr->ip_ttl--; if (ip_hdr->ip_ttl == 0) { sr_send_icmp(sr, packet, len, 11, 0); return; } ip_hdr->ip_sum = 0; ip_hdr->ip_sum = cksum(ip_hdr, ip_hdr->ip_hl * 4); struct sr_rt *rt = sr_longest_prefix_match_lookup(sr, ip_hdr->ip_dst); if (!rt) { sr_send_icmp(sr, packet, len, 3, 0); return; } struct sr_if *oiface = sr_get_interface(sr, rt->interface); sr_lookup_and_send(sr, packet, len, oiface, rt->gw.s_addr); }
void sr_handleicmp(struct sr_instance* sr, uint8_t * packet/* lent */, unsigned int len, struct sr_if* iface/* lent */) { if (len < ( sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_hdr_t) )) { fprintf(stderr, "Failed to process ICMP header, insufficient length\n"); return; } sr_icmp_hdr_t *icmp_hdr = (sr_icmp_hdr_t *)( packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); /* Sanity Check */ if (cksum(icmp_hdr, packet + len - (uint8_t*)icmp_hdr) != CKSUM_CORRECT) { fprintf(stderr, "sr_handleicmp: Corrupted ICMP Header, dropping packet.\n"); return; } /* Ignore anything other than ICMP Echo Request */ if (icmp_hdr->icmp_type != 8) { fprintf(stderr, "sr_handleicmp: ICMP Reply dropped.\n"); return; } sr_send_icmp(sr, packet, len, iface, ICMP_TYPE_ECHO_REPLY, 0); }
/*--------------------------------------------------------------------- * Method: forward_ip_pkt(struct sr_instance* sr, sr_ip_hdr *ip_hdr) * Scope: Internal * * Forwards an IP packet to its next hop. * *---------------------------------------------------------------------*/ void forward_ip_pkt(struct sr_instance* sr, struct sr_ip_hdr *ip_hdr) { uint8_t *fwd_ip_pkt; unsigned int len; /* Update the ip header ttl. */ ip_hdr->ip_ttl--; /* If the ttl is equal to 0, send an ICMP Time exceeded response and return. */ len = ip_len(ip_hdr); if (ip_hdr->ip_ttl == 0) { sr_send_icmp(sr, (uint8_t *)ip_hdr, len, ICMP_TIME_EXCEEDED_TYPE, 0); return; } /* Update the checksum. */ ip_hdr->ip_sum = 0; ip_hdr->ip_sum = cksum(ip_hdr, ip_ihl(ip_hdr)); /* Make a copy, encapsulate and send it on. */ fwd_ip_pkt = malloc(len); memcpy(fwd_ip_pkt, ip_hdr, len); sr_encap_and_send_pkt(sr, fwd_ip_pkt, len, ip_hdr->ip_dst, 1, ethertype_ip); free(fwd_ip_pkt); }
/*--------------------------------------------------------------------- * Method: process_ip(struct sr_instance* sr, * uint8_t * packet, * unsigned int len, * char* interface) * Scope: Internal * * Processes a received IP packet. Takes in a raw ethernet packet. * *---------------------------------------------------------------------*/ void process_ip(struct sr_instance* sr, uint8_t * packet, unsigned int len, char* interface) { struct sr_ip_hdr *ip_hdr; /* Return if it is not a valid ip packet */ if (!valid_ip(packet, len)) return; /* Is it destined for me?! */ ip_hdr = ip_header(packet); if (sr_interface_ip_match(sr, ip_hdr->ip_dst)) { /* Process ICMP. */ if (ip_hdr->ip_p == ip_protocol_icmp) { process_icmp(sr, ip_hdr); /* If it's TCP/UDP, send ICMP port unreachable. */ } else { sr_send_icmp(sr, (uint8_t *)ip_hdr, ip_len(ip_hdr), ICMP_UNREACHABLE_TYPE, ICMP_PORT_CODE); } /* Forward it. */ } else { forward_ip_pkt(sr, ip_hdr); } }
void sr_handle_icmp(struct sr_instance *sr, uint8_t *packet, unsigned int len) { assert(sr); assert(packet); sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)); if (len < sizeof(sr_ethernet_hdr_t) + (ip_hdr->ip_hl * 4) + sizeof(sr_icmp_hdr_t)) { fprintf(stderr, "Failed to process ICMP header, insufficient length\n"); return; } sr_icmp_hdr_t *icmp_hdr = (sr_icmp_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t) + (ip_hdr->ip_hl * 4)); uint16_t received_cksum = icmp_hdr->icmp_sum; icmp_hdr->icmp_sum = 0; uint16_t computed_cksum = cksum(icmp_hdr, ntohs(ip_hdr->ip_len) - (ip_hdr->ip_hl * 4)); icmp_hdr->icmp_sum = received_cksum; if (received_cksum != computed_cksum) { fprintf(stderr, "Failed to process ICMP header, incorrect checksum\n"); return; } if (icmp_hdr->icmp_type == 8 && icmp_hdr->icmp_code == 0) { sr_send_icmp(sr, packet, len, 0, 0); } }
void sr_handleincoming(struct sr_instance* sr, uint8_t * packet/* lent */, unsigned int len, struct sr_if* iface/* lent */) { /* Reply everything with 0x3 = Port unreachable */ sr_send_icmp(sr, packet, len, iface, ICMP_TYPE_UNREACHABLE, ICMP_UNREACHABLE_PORT); }
/*--------------------------------------------------------------------- * Method: process_icmp(struct sr_instance* sr, sr_ip_hdr *ip_hdr) * Scope: Internal * * This function processes an ICMP packet that was destined for the * router. * *---------------------------------------------------------------------*/ void process_icmp(struct sr_instance* sr, struct sr_ip_hdr *ip_hdr) { /* Validate icmp. Drop if it not an echo request or bad checksum. */ if (!valid_icmp(ip_hdr)) return; /* Send icmp echo reply. */ sr_send_icmp(sr, (uint8_t *)ip_hdr, ip_len(ip_hdr), ICMP_ECHO_REPLY_CODE, 0); }
void sr_handle_ip(struct sr_instance *sr, uint8_t *packet, unsigned int len, struct sr_if *iface) { assert(sr); assert(packet); assert(iface); if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)) { fprintf(stderr, "Failed to process IP header, insufficient length\n"); return; } sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)); if (len < sizeof(sr_ethernet_hdr_t) + (ip_hdr->ip_hl * 4)) { fprintf(stderr, "Failed to process IP header, insufficient length\n"); return; } /* verify checksum */ uint16_t received_cksum = ip_hdr->ip_sum; ip_hdr->ip_sum = 0; uint16_t computed_cksum = cksum(ip_hdr, ip_hdr->ip_hl * 4); ip_hdr->ip_sum = received_cksum; if (received_cksum != computed_cksum) { fprintf(stderr, "Failed to process IP header, incorrect checksum\n"); return; } struct sr_if *diface = sr_get_interface_from_ip(sr, ip_hdr->ip_dst); if (!diface) { sr_forward_ip(sr, packet, len); } else { if (ip_hdr->ip_p == ip_protocol_icmp) { sr_handle_icmp(sr, packet, len); } else if (ip_hdr->ip_p == 0x0006 || ip_hdr->ip_p == 0x0011) { sr_send_icmp(sr, packet, len, 3, 3); } } }
void sr_forwardpkt(struct sr_instance* sr, uint8_t * pkt/* lent */, unsigned int len, struct sr_if* iface/* lent */) { assert(sr); assert(pkt); assert(iface); sr_ip_hdr_t *iphdr = (sr_ip_hdr_t *)(pkt + sizeof(sr_ethernet_hdr_t)); /* Check TTL */ if (iphdr->ip_ttl <= 1) { /* TTL Exceeded */ fprintf(stderr, "sr_forwardpkt: TTL Exceeded!\n"); sr_send_icmp(sr, pkt, len, iface, ICMP_TYPE_TTLEXCEED, 0); /* ICMP Type 11 0x0: Time exceeded */ return; } /* Make a copy */ uint8_t *outpkt = malloc(len); memcpy(outpkt, pkt, len); /* Decrement TTL */ iphdr = (sr_ip_hdr_t *)(outpkt + sizeof(sr_ethernet_hdr_t)); --iphdr->ip_ttl; /* Recalculate checksum */ iphdr->ip_sum = 0x0; iphdr->ip_sum = cksum((void*)iphdr, sizeof(sr_ip_hdr_t)); sr_send_inetpkt(sr, outpkt, len, iface, 0); free(outpkt); }
void sr_handle_arpreq(struct sr_instance *sr, struct sr_arpreq *req) { assert(sr); assert(req); if (difftime(time(NULL), req->sent) >= 1.0) { if (req->times_sent >= 5) { struct sr_packet *pkt = NULL; sr_ethernet_hdr_t *eth_hdr = NULL; struct sr_if *iface = NULL; pkt = req->packets; while (pkt) { eth_hdr = (sr_ethernet_hdr_t *)(pkt->buf); iface = sr_get_interface_from_addr(sr, eth_hdr->ether_dhost); /* do not send an ICMP message for an ICMP message */ if (iface) { sr_send_icmp(sr, pkt->buf, pkt->len, 3, 1); } pkt = pkt->next; } sr_arpreq_destroy(&(sr->cache), req); } else { struct sr_if *oiface = sr_get_interface(sr, req->packets->iface); sr_send_arp_request(sr, oiface, req->ip); req->sent = time(NULL); req->times_sent++; } } } /* -- sr_handle_arpreq -- */
void sr_handle_ip(struct sr_instance* sr, uint8_t* packet, unsigned int len, char* iface) { struct sr_if* interface = sr_get_interface(sr, iface); struct sr_ip_hdr* ipHeader = (struct sr_ip_hdr*)(packet + sizeof(sr_ethernet_hdr_t)); /* Verify the checksum */ uint16_t check = cksum(ipHeader, sizeof(sr_ip_hdr_t)); if(check != 0xFFFF) { printf("Packet Corrupted!\n"); return; } /* Check destination */ if(ipHeader->ip_dst == interface->ip) { /* Check protocol */ if(ipHeader->ip_p == ip_protocol_icmp) { /* Process ICMP message */ struct sr_icmp_hdr* icmpHeader = (struct sr_icmp_hdr*)(packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); if(icmpHeader->icmp_type == icmp_type_echorequest) { printf("Echo Request...\n"); sr_send_icmp(sr, packet, len, icmp_type_echoreply, 0); } } else { /* Send port unreachable ICMP message */ printf("Port Unreachable\n"); sr_send_icmp(sr, packet, len, icmp_type_destunreachable, icmp_code_portunreachable); } } else { printf("Processing Packet for forwarding...\n"); /* Forward the packet accordingly */ /* Decrement TTL */ ipHeader->ip_ttl = ipHeader->ip_ttl-1; if(ipHeader->ip_ttl == 0) { /* Send ICMP time exceeded */ printf("TTL expired! Returning ICMP timeexceeded\n"); sr_send_icmp(sr, packet, len, icmp_type_timeexceeded, 0); return; } /* Check if the destination is in our routing table */ char * iface = sr_get_routing_entry_interface(sr, ipHeader->ip_dst); if(iface == NULL) { /* Send a network unreachable message */ printf("No route! Returning ICMP netUnreachable\n"); sr_send_icmp(sr, packet, len, icmp_type_destunreachable, icmp_code_netunreachable); return; } /* Recalculate the checksum */ ipHeader->ip_sum = 0x0000; ipHeader->ip_sum = cksum(ipHeader, sizeof(sr_ip_hdr_t)); /* Look up in the ARP table */ struct sr_arpentry * arpCacheEntry = sr_arpcache_lookup(&(sr->cache), ipHeader->ip_dst); /* At this point we know we're going to send it, so make a copy */ uint8_t* copy = (uint8_t*)malloc(len); memcpy(copy, packet, len); struct sr_ip_hdr* copyIPHeader = (struct sr_ip_hdr*)(copy + sizeof(sr_ethernet_hdr_t)); /* See if there was an entry */ if(arpCacheEntry == NULL) { /* No entry, so queue up for ARP request */ printf("ARP MISS!\n"); sr_arpcache_queuereq(&(sr->cache), copyIPHeader->ip_dst, copy, len, iface); printf("Added ARP req for "); print_addr_ip_int(htonl(copyIPHeader->ip_dst)); printf(" to queue\n"); return; } printf("ARP HIT!\n"); /* Forward the IP message using the ARP Cache info */ sr_send_ip(sr, copy, len, iface); free(arpCacheEntry); } }/* -- sr_handle_ip -- */
/* Find route and send out inet packet. */ void sr_send_inetpkt(struct sr_instance* sr, uint8_t * pkt/* lent */, unsigned int len, struct sr_if* src_iface/* lent */, int inited_by_router) { sr_ip_hdr_t *iphdr = (sr_ip_hdr_t *)(pkt + sizeof(sr_ethernet_hdr_t)); /* Find route */ struct sr_rt* nexthop = sr_findroute(sr, iphdr->ip_dst); if (nexthop == NULL) { /* No route found */ fprintf(stderr, "sr_send_inetpkt: No route found!\n"); if (inited_by_router) return; sr_send_icmp(sr, pkt, len, src_iface, ICMP_TYPE_UNREACHABLE, ICMP_UNREACHABLE_NET); return; } /* Next hop is immediate host */ uint32_t nexthop_ip = (nexthop->gw.s_addr ? nexthop->gw.s_addr : iphdr->ip_dst); /* ARP Lookup */ struct sr_arpentry *arpentry = sr_arpcache_lookup(&sr->cache, nexthop_ip); if (arpentry == NULL) { /* No ARP entry found in cache, request one & queue packet */ fprintf(stderr, "sr_send_inetpkt: No ARP entry found in cache, queuing packet.\n"); struct sr_arpreq *arpreq = sr_arpcache_queuereq( &sr->cache, nexthop_ip, pkt, len, nexthop->interface); /* Send ARP request if not already sent */ if (arpreq->sent == 0) { sr_send_arpreq(sr, arpreq->ip, sr_get_interface(sr, nexthop->interface)); arpreq->sent = time(NULL); ++arpreq->times_sent; } return; } struct sr_if* iface_nexthop = sr_get_interface(sr, nexthop->interface); assert(iface_nexthop); sr_fill_eth(pkt, (uint8_t *)arpentry->mac, (uint8_t *)iface_nexthop->addr, ethertype_ip); free(arpentry); /* Send frame to next hop */ fprintf(stderr, "sr_send_inetpkt: Ready to send frame.\n"); /* Send INet Packet */ sr_send_packet(sr, pkt, len, iface_nexthop->name); }
/*--------------------------------------------------------------------- * Method: sr_encap_and_send_pkt(struct sr_instance* sr, * uint8_t *packet, * unsigned int len, * uint32_t dip, * int send_icmp, * sr_ethertype type) * Scope: Global * * Sends a packet of length len and destination ip address dip, by * looking up the shortest prefix match of the dip (net byte order). * If the destination is not found, it sends an ICMP host unreachable. * If it finds a match, it then checks the arp cache to find the * associated hardware address. If the hardware address is found it * sends it, otherwise it queues the packet and sends an ARP request. * *---------------------------------------------------------------------*/ void sr_encap_and_send_pkt(struct sr_instance* sr, uint8_t *packet, unsigned int len, uint32_t dip, int send_icmp, enum sr_ethertype type) { struct sr_arpentry *arp_entry; struct sr_arpreq *arp_req; struct sr_ethernet_hdr eth_hdr; uint8_t *eth_pkt; struct sr_if *interface; struct sr_rt *rt; unsigned int eth_pkt_len; /* Look up shortest prefix match in your routing table. */ rt = sr_longest_prefix_match(sr, ip_in_addr(dip)); /* If the entry doesn't exist, send ICMP host unreachable and return if necessary. */ if (rt == 0) { if (send_icmp) sr_send_icmp(sr, packet, len, ICMP_UNREACHABLE_TYPE, ICMP_NET_CODE); return; } /* Fetch the appropriate outgoing interface. */ interface = sr_get_interface(sr, rt->interface); /* If there is already an arp entry in the cache, send now. */ arp_entry = sr_arpcache_lookup(&sr->cache, rt->gw.s_addr); if (arp_entry || type == ethertype_arp) { /* Create the ethernet packet. */ eth_pkt_len = len + sizeof(eth_hdr); eth_hdr.ether_type = htons(type); /* Destination is broadcast if it is an arp request. */ if (type == ethertype_arp && ((struct sr_arp_hdr *)packet)->ar_op == htons(arp_op_request)) memset(eth_hdr.ether_dhost, 255, ETHER_ADDR_LEN); /* Destination is the arp entry mac if it is an ip packet or and are reply. */ else memcpy(eth_hdr.ether_dhost, arp_entry->mac, ETHER_ADDR_LEN); memcpy(eth_hdr.ether_shost, interface->addr, ETHER_ADDR_LEN); eth_pkt = malloc(eth_pkt_len); memcpy(eth_pkt, ð_hdr, sizeof(eth_hdr)); memcpy(eth_pkt + sizeof(eth_hdr), packet, len); sr_send_packet(sr, eth_pkt, eth_pkt_len, rt->interface); free(eth_pkt); if (arp_entry) free(arp_entry); /* Otherwise add it to the arp request queue. */ } else { eth_pkt = malloc(len); memcpy(eth_pkt, packet, len); arp_req = sr_arpcache_queuereq(&sr->cache, rt->gw.s_addr, eth_pkt, len, rt->interface); sr_arpreq_handle(sr, arp_req); free(eth_pkt); } }