static struct frame_t *generate_icmp_error(struct frame_t *incoming, uint16_t icmp_type, uint16_t icmp_code){ assert (incoming); /* need to get pointers to various things */ /* get some useful info about lengths */ size_t icmp_data_len = incoming->ip_hl + 2 * sizeof(uint32_t); //length of echo data, in bytes if (incoming->ip_len - incoming->ip_hl < 8 ) icmp_data_len = incoming->ip_len; /* create and fill out frame_t */ struct frame_t *outgoing = malloc(sizeof(struct frame_t)); assert(outgoing); outgoing->len = sizeof(struct sr_ethernet_hdr) + sizeof(struct ip) + sizeof(struct icmp_hdr) + icmp_data_len; outgoing->frame = malloc(outgoing->len); outgoing->ether_header = (struct sr_ethernet_hdr *)outgoing->frame; assert(outgoing->frame); outgoing->ip_header = (struct ip *)(outgoing->frame + sizeof(struct sr_ethernet_hdr)); outgoing->arp_header = NULL; assert(outgoing->ip_header); outgoing->icmp_header = (struct icmp_hdr *)((void *)outgoing->ip_header + incoming->ip_hl); assert(outgoing->icmp_header); outgoing->in_or_out = OUT; outgoing->MAC_set = 1; outgoing->from_ip = incoming->to_ip; outgoing->to_ip = incoming->from_ip; memcpy(outgoing->from_MAC, incoming->to_MAC, ETHER_ADDR_LEN); memcpy(outgoing->to_MAC, incoming->from_MAC, ETHER_ADDR_LEN); outgoing->iface = incoming->iface; outgoing->ip_len = outgoing->len - sizeof(struct sr_ethernet_hdr); outgoing->ip_hl = IP_HDR_LEN; /* fill out icmp header */ outgoing->icmp_header->icmp_type = icmp_type; outgoing->icmp_header->icmp_code = icmp_code; outgoing->icmp_header->icmp_unused = 0; outgoing->icmp_header->icmp_sum = 0; /* copy data into header: packet includes IP header, ICMP header, and beginning of original packet */ void *icmp_data = (void *)outgoing->icmp_header + sizeof(struct icmp_hdr); assert(icmp_data); memcpy(icmp_data, incoming->ip_header, icmp_data_len); compute_icmp_checksum(outgoing); ip_header_create(outgoing); encapsulate(outgoing); return outgoing; }
/*--------------------------------------------------------------------- * Method: arp_create; * *---------------------------------------------------------------------*/ static struct frame_t *arp_create(struct sr_instance *sr, struct frame_t *incoming, struct sr_if *iface, unsigned short op) { assert(incoming); //create and fill out frame_t struct frame_t *outgoing = malloc(sizeof(struct frame_t)); assert(outgoing); outgoing->len = sizeof(struct sr_ethernet_hdr) + sizeof(struct sr_arphdr); outgoing->frame = malloc(outgoing->len); outgoing->ether_header = (struct sr_ethernet_hdr *)outgoing->frame; outgoing->arp_header = (struct sr_arphdr *)(outgoing->frame + sizeof(struct sr_ethernet_hdr)); outgoing->ip_header = NULL; outgoing->icmp_header = NULL; outgoing->ip_len = 0; outgoing->ip_hl = 0; outgoing->iface = iface; outgoing->MAC_set = 1; //fill out constant parts of arp_header outgoing->arp_header->ar_hrd = htons(1); outgoing->arp_header->ar_pro = htons(ETHERTYPE_IP); outgoing->arp_header->ar_hln = ETHER_ADDR_LEN; outgoing->arp_header->ar_pln = 4; //fill out MAC and IP addresses memcpy(outgoing->arp_header->ar_tha, incoming->from_MAC, ETHER_ADDR_LEN); //this will give nonsense for an arp request which is fine--unless we need a broadcast address, but I don't think so memcpy(outgoing->arp_header->ar_sha, iface->addr, ETHER_ADDR_LEN); memcpy(outgoing->from_MAC, iface->addr, ETHER_ADDR_LEN); if (op == ARP_REQUEST){ struct sr_rt *route_entry = rt_match(sr, incoming->to_ip); outgoing->arp_header->ar_tip = route_entry->gw.s_addr; //incoming is an IP datagram, we want to know MAC of next hop in routing table outgoing->to_ip = outgoing->arp_header->ar_tip; memset(outgoing->to_MAC, 0xFF, ETHER_ADDR_LEN); //set outgoing MAC to broadcast address } else{ outgoing->arp_header->ar_tip = incoming->from_ip; //incoming is an ARP request, we want to send back to that IP outgoing->to_ip = incoming->from_ip; memcpy(outgoing->to_MAC, incoming->from_MAC, ETHER_ADDR_LEN); } outgoing->arp_header->ar_sip = outgoing->iface->ip; outgoing->from_ip = outgoing->iface->ip; outgoing->arp_header->ar_op = htons(op); encapsulate(outgoing); assert(outgoing); return outgoing; }
static struct frame_t *generate_icmp_echo(struct frame_t *incoming){ assert(incoming); /* create and fill out frame_t */ struct frame_t *outgoing = malloc(sizeof(struct frame_t)); assert(outgoing); outgoing->frame = malloc(incoming->len); assert(outgoing->frame); memcpy(outgoing->frame, incoming->frame, incoming->len); outgoing->ether_header = (struct sr_ethernet_hdr *)outgoing->frame; outgoing->ip_header = (struct ip *)(outgoing->frame + sizeof(struct sr_ethernet_hdr)); outgoing->arp_header = NULL; assert(outgoing->ip_header); outgoing->icmp_header = (struct icmp_hdr *)((void *)outgoing->ip_header + incoming->ip_hl); assert(outgoing->icmp_header); outgoing->in_or_out = OUT; memcpy(outgoing->from_MAC, incoming->to_MAC, ETHER_ADDR_LEN); memcpy(outgoing->to_MAC, incoming->from_MAC, ETHER_ADDR_LEN); outgoing->MAC_set = 1; outgoing->from_ip = incoming->to_ip; outgoing->to_ip = incoming->from_ip; outgoing->iface = incoming->iface; outgoing->len = incoming->len; outgoing->ip_len = incoming->ip_len; outgoing->ip_hl = incoming->ip_hl; /* fill out icmp header */ outgoing->icmp_header->icmp_type = ECHO_REPLY; outgoing->icmp_header->icmp_sum = 0; /* fill out other headers too */ compute_icmp_checksum(outgoing); //again, this will need updating, ideally it'll only take outgoing as argument ip_header_create(outgoing); //this function only create headers for ICMP packets encapsulate(outgoing); //memory and such already allocated, just fills in fields appropriately return outgoing; }
int *get_fdata(char * data, size_t len, modulation m) { frame *frm = encapsulate(data, len); char *enc = hm_encode((char *) frm, FRAME_SIZE); int * vbuf; switch (m) { case AM: vbuf = modulate_am(enc); break; case FM: vbuf = modulate_fm(enc); break; } free(enc); free(frm); return vbuf; }
// app data -> SSL -> protocol encapsulation -> reliability layer -> network void down_stack_app() { if (ssl_started_) { // push app-layer cleartext through SSL object while (!app_write_queue.empty()) { BufferPtr& buf = app_write_queue.front(); try { const ssize_t size = ssl_->write_cleartext_unbuffered(buf->data(), buf->size()); if (size == SSLContext::SSL::SHOULD_RETRY) break; } catch (...) { if (stats) stats->error(Error::SSL_ERROR); invalidate(Error::SSL_ERROR); throw; } app_write_queue.pop_front(); } // encapsulate SSL ciphertext packets while (ssl_->read_ciphertext_ready() && rel_send.ready()) { typename ReliableSend::Message& m = rel_send.send(*now); m.packet = PACKET(ssl_->read_ciphertext()); // encapsulate packet try { encapsulate(m.id(), m.packet); } catch (...) { if (stats) stats->error(Error::ENCAPSULATION_ERROR); invalidate(Error::ENCAPSULATION_ERROR); throw; } // transmit it net_send(m.packet, NET_SEND_SSL); } } }
/*--------------------------------------------------------------------- * Method: arpq_entry_clear; * *---------------------------------------------------------------------*/ static void arpq_entry_clear(struct sr_instance *sr, struct arp_queue *queue, struct arpq_entry *entry, unsigned char *d_ha) { assert(sr); assert(queue); assert(entry); assert(d_ha); struct queued_packet *qpacket; while( qpacket = ((entry->arpq_packets).first) ) { struct frame_t *outgoing = qpacket->outgoing; memcpy(outgoing->from_MAC, outgoing->iface->addr, ETHER_ADDR_LEN); memcpy(outgoing->to_MAC, d_ha, ETHER_ADDR_LEN); encapsulate(outgoing); printf("cleared packet from queue\n"); sr_send_packet(sr, (uint8_t *)outgoing->frame, outgoing->len, outgoing->iface->name); if( !((entry->arpq_packets).first = qpacket->next) ){ (entry->arpq_packets).first = NULL; (entry->arpq_packets).last = NULL; //need to set both so destroy_arpq_entry works right } destroy_frame_t(outgoing); free(qpacket); } if (entry->prev) (entry->prev)->next = entry->next; else queue->first = entry->next; if (entry->next) (entry->next)->prev = entry->prev; else queue->last = entry->prev; if (entry->prev) destroy_arpq_entry(entry); return; }
// raw app data -> protocol encapsulation -> reliability layer -> network void down_stack_raw() { while (!raw_write_queue.empty() && rel_send.ready()) { typename ReliableSend::Message& m = rel_send.send(*now); m.packet = raw_write_queue.front(); raw_write_queue.pop_front(); // encapsulate packet try { encapsulate(m.id(), m.packet); } catch (...) { if (stats) stats->error(Error::ENCAPSULATION_ERROR); invalidate(Error::ENCAPSULATION_ERROR); throw; } // transmit it net_send(m.packet, NET_SEND_RAW); } }
void sr_handlepacket(struct sr_instance* sr, uint8_t* packet/* lent */, unsigned int len, char* interface/* lent */) { /* REQUIRES */ assert(sr); assert(packet); assert(interface); printf("*** -> Received packet of length %d \n",len); if( len < ETHER_HDR_LEN ) { printf("Bogus packet length\n"); return; } struct sr_if *iface; struct frame_t *incoming = create_frame_t(sr, packet, len, interface); struct frame_t *outgoing = NULL; /* First, deal with ARP cache timeouts */ arp_cache_flush(&sr_arp_cache); arp_queue_flush(sr, &sr_arp_queue); /* Do we only need to cache ARP replies, or src MAC/IP on regular IP packets too, etc? */ /* Also, do we need to worry about fragmentation? */ /* Then actually handle the packet */ /* Start by determining protocol */ if (incoming->ip_header){ /* sanity checks */ if ( incoming->ip_header->ip_v != 4 ){ printf("IP packet not IPv4\n"); return; } compute_ip_checksum(incoming); /* Check the checksum */ if( incoming->ip_header->ip_sum != 0 ) { fprintf(stderr, "IP checksum incorrect, packet was dropped\n"); return; } //set checksum back to what it was compute_ip_checksum(incoming); /* Are we the destination? */ if (iface = if_dst_check(sr, incoming->to_ip)){ //we could change this to just take incoming and then get to_ip /* Is this an ICMP packet? */ if (incoming->icmp_header){ printf("received ICMP datagram\n"); compute_icmp_checksum(incoming); if(incoming->icmp_header->icmp_type == ECHO_REQUEST && incoming->icmp_header->icmp_sum == 0){ outgoing = generate_icmp_echo(incoming); printf("received ICMP echo request\n"); } else printf("Dropped packet--we don't deal with that code, or invalid checksum\n"); } else{ outgoing = generate_icmp_error(incoming, DEST_UNREACH, PORT_UNREACH); printf("A packet for me! Flattering, but wrong.\n"); } } else { /* Has it timed out? */ if (incoming->ip_header->ip_ttl <= 0){ int err = 0; //make sure it's not an ICMP error packet alrady if (incoming->ip_header->ip_p == IPPROTO_ICMP){ incoming->icmp_header = ((void *) incoming->ip_header + incoming->ip_hl); uint8_t code = incoming->icmp_header->icmp_type; //don't send ICMP error messages about ICMP error messages if (code == DEST_UNREACH || code == TIME_EXCEEDED || code == 12 || code == 31) //12 and 31 indicate bad IP header and datagram conversion error, respectively err = 1; } if (!err){ outgoing = generate_icmp_error(incoming, TIME_EXCEEDED, TIME_INTRANSIT); printf("Slowpoke. TTL exceeded.\n"); } } else { /* update and forward packet; if necessary, add it to queue */ struct arpc_entry *incache; uint32_t arpc_ip; outgoing = update_ip_hdr(sr, incoming, &arpc_ip); incache = arp_cache_lookup(sr_arp_cache.first, arpc_ip); if (!incache){ struct arpq_entry *entry = arp_queue_lookup(sr_arp_queue.first, outgoing->to_ip); struct arpq_entry *temp_entry = NULL; if ( entry ){ //if we've already sent an ARP request about this IP if( time(NULL) - 1 > entry->arpq_last_req ) { if(entry->arpq_num_reqs >= ARP_MAX_REQ) { printf("Too many ARP requests\n"); arpq_packets_icmpsend(sr, &entry->arpq_packets); destroy_arpq_entry(entry); return; } else if (entry->arpq_packets.first) { struct frame_t *arp_req; struct queued_packet *old_packet = entry->arpq_packets.first; entry->arpq_last_req = time(NULL); entry->arpq_num_reqs++; } } assert( (entry->arpq_packets).first ); if (!arpq_add_packet(entry, outgoing, len, incoming->from_MAC, incoming->iface)) printf("ARP queue packet add failed\n"); else printf("added packet to queue\n"); } /* else, there are no outstanding ARP requests for this particular IP */ else { printf("outgoing ip is %d\n", ntohl(outgoing->to_ip)); temp_entry = arpq_add_entry(&sr_arp_queue, outgoing->iface, outgoing, outgoing->to_ip, outgoing->ip_len, incoming->from_MAC, incoming->iface); } free(outgoing->frame); /* make ARP request */ outgoing = arp_create(sr, outgoing, outgoing->iface, ARP_REQUEST); //send datagram will now point to an ARP packet, not to the IP datagram if (temp_entry){ temp_entry->arpq_next_hop = outgoing->to_ip; } printf("sending ARP request\n"); } else{ printf("got the MAC, can actually send this packet\n"); memcpy(outgoing->to_MAC, incache->arpc_mac, ETHER_ADDR_LEN); encapsulate(outgoing); } } } } else if ( incoming->arp_header ) { printf("received ARP packet\n"); struct sr_arphdr *arp_header = incoming->arp_header; uint8_t in_cache = 0; struct arpc_entry *arpc_ent = arp_cache_lookup(sr_arp_cache.first, arp_header->ar_sip); printf("checking the cache\n"); if( arpc_ent ) { arp_cache_update(arpc_ent, arp_header->ar_sha); printf("updated cache\n"); in_cache = 1; } struct sr_if *target_if = if_dst_check(sr, arp_header->ar_tip); printf("checking the target\n"); if( target_if ) { printf("It's for us\n"); if( !in_cache ) { if( arp_cache_add(&sr_arp_cache, arp_header->ar_sha, arp_header->ar_sip) ) { printf("added to cache\n"); printf("ip is %d\n", ntohl(arp_header->ar_sip)); struct arpq_entry *new_ent; if( new_ent = arpq_next_hop_lookup(sr_arp_queue.first, arp_header->ar_sip) ) arpq_entry_clear(sr, &sr_arp_queue, new_ent, arp_header->ar_sha); } else perror("ARP request not added to cache"); } if( ntohs(arp_header->ar_op) == ARP_REQUEST ){ outgoing = arp_create(sr, incoming, incoming->iface, ARP_REPLY); printf("created ARP reply\n"); assert(outgoing); } } } else perror("Unknown protocol"); //send datagram, if appropriate if (outgoing != NULL){ sr_send_packet(sr, (uint8_t *)outgoing->frame, outgoing->len, outgoing->iface->name); printf("sent packet of length %d on iface %s\n", outgoing->len, outgoing->iface->name); } if (outgoing != NULL) destroy_frame_t(outgoing); }/* end sr_handlepacket */