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); } } } } }
int main (int argc, char **argv) { FILE *originalFile; int numOfBytesRead=1; int c; FILE *fileToWrite; char * sumOfOptions = "usage: bsplit [-x] [-h] [-b SIZE] FILE"; int maxSizeFromUser=1024; int numOfFile=1; char numOfFileStr[50]; unsigned int originalFileCksum = 0; char* fileName = argv[argc-1]; originalFile = fopen( fileName, "r"); /*opens the original file */ unsigned int currCksum = 0; while ((c = getopt (argc, argv, "xhb:")) != -1) { switch (c) { case 'x': originalFileCksum = cksum(originalFile); printf("%d\n", originalFileCksum ); break; case 'h': printf("%s\n", sumOfOptions ); exit(0); break; case 'b': maxSizeFromUser = atoi(optarg); break; default: abort (); } } unsigned int arrayForNewFile [(maxSizeFromUser+3)/4] ; while(numOfBytesRead) { numOfBytesRead = fread(arrayForNewFile, 1, maxSizeFromUser, originalFile); /*reads maxSizeFromUser at most bytes- 1 byte each time and saves it in arrayForNewFile*/ if(numOfBytesRead!=0) { if(numOfFile<10) { sprintf(numOfFileStr , "%s.0%d", fileName, numOfFile); /*adds the numOfFile into "FILE.0%d" and saves the resulting string in numOfFileStr */ } else { sprintf(numOfFileStr , "%s.%d", fileName, numOfFile); /*adds the numOfFile into "FILE.%d" and saves the resulting string in numOfFileStr */ } /* printf ("Characters: %c %d \n", 'a', 65);*/ fileToWrite = fopen( numOfFileStr, "w+"); /*creates a new file to write&read to, called numOfFileStr*/ fwrite ( &currCksum, sizeof(currCksum), 1, fileToWrite);/*write 4 bytes of "0" into the beggining of the new file*/ fwrite ( arrayForNewFile, 1, numOfBytesRead, fileToWrite); /*write into the new file all the chunk data from the arrayForNewFile*/ fseek( fileToWrite, sizeof(currCksum) , SEEK_SET ); /*Sets the position indicator associated with the stream to a new position- 4 bytes after the beggining of the file */ currCksum = cksum(fileToWrite); /*calculates the cksum of the new file */ rewind ( fileToWrite ); /* Set position of stream to the beginning */ fwrite ( &currCksum, sizeof(currCksum), 1, fileToWrite); /*write the currCksum in the beggining of the fileToWrite- override the "0" that was there */ currCksum = 0; ++numOfFile; fclose(fileToWrite); } } fclose (originalFile); return 0; }
/* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(struct mbuf *m) { register struct ip *ip; int hlen; DEBUG_CALL("ip_input"); DEBUG_ARG("m = %lx", (long)m); DEBUG_ARG("m_len = %d", m->m_len); STAT(ipstat.ips_total++); if (m->m_len < sizeof (struct ip)) { STAT(ipstat.ips_toosmall++); return; } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { STAT(ipstat.ips_badvers++); goto bad; } hlen = ip->ip_hl << 2; if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */ STAT(ipstat.ips_badhlen++); /* or packet too short */ goto bad; } /* keep ip header intact for ICMP reply * ip->ip_sum = cksum(m, hlen); * if (ip->ip_sum) { */ if(cksum(m,hlen)) { STAT(ipstat.ips_badsum++); goto bad; } /* * Convert fields to host representation. */ NTOHS(ip->ip_len); if (ip->ip_len < hlen) { STAT(ipstat.ips_badlen++); goto bad; } NTOHS(ip->ip_id); NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_len < ip->ip_len) { STAT(ipstat.ips_tooshort++); goto bad; } if (slirp_restrict) { if (ip_geth(ip->ip_dst) != special_addr_ip) { if (ip_getn(ip->ip_dst) == 0xffffffffu && ip->ip_p != IPPROTO_UDP) goto bad; } else { int host = ip_geth(ip->ip_dst) & 0xff; struct ex_list *ex_ptr; if (host == 0xff) goto bad; for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) if (ex_ptr->ex_addr == host) break; if (!ex_ptr) goto bad; } } /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ if(ip->ip_ttl==0 || ip->ip_ttl==1) { icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); goto bad; } /* * Process options and, if not destined for us, * ship it on. ip_dooptions returns 1 when an * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ /* We do no IP options */ /* if (hlen > sizeof (struct ip) && ip_dooptions(m)) * goto next; */ /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. * (We could look in the reassembly queue to see * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) * * XXX This should fail, don't fragment yet */ if (ip->ip_off &~ IP_DF) { register struct ipq *fp; struct qlink *l; /* * Look for queue of fragments * of this datagram. */ for (l = ipq.ip_link.next; l != &ipq.ip_link; l = l->next) { fp = container_of(l, struct ipq, ip_link); if (ip->ip_id == fp->ipq_id && ip_equal(ip->ip_src, fp->ipq_src) && ip_equal(ip->ip_dst, fp->ipq_dst) && ip->ip_p == fp->ipq_p) goto found; } fp = NULL; found: /* * Adjust ip_len to not reflect header, * set ip_mff if more fragments are expected, * convert offset of this to bytes. */ ip->ip_len -= hlen; if (ip->ip_off & IP_MF) ip->ip_tos |= 1; else ip->ip_tos &= ~1; ip->ip_off <<= 3; /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ if (ip->ip_tos & 1 || ip->ip_off) { STAT(ipstat.ips_fragments++); ip = ip_reass(ip, fp); if (ip == NULL) return; STAT(ipstat.ips_reassembled++); m = dtom(ip); } else if (fp) ip_freef(fp); } else
void ip_handlepacketforme(struct sr_instance *sr, sr_ip_hdr_t *ip_hdr, char *interface) { struct sr_arpreq *req; struct sr_arpentry *arp_entry; uint8_t *cache_packet; uint16_t total_len; uint16_t icmp_len; uint32_t dst; uint8_t icmp_type; uint8_t icmp_code; /* Check whether ICMP echo request or TCP/UDP */ if (ip_hdr->ip_p == ip_protocol_icmp){ dst = ip_hdr->ip_src; ip_hdr->ip_src = ip_hdr->ip_dst; ip_hdr->ip_dst = dst; /* Modify the ICMP reply packet */ sr_icmp_hdr_t *icmp_hdr_ptr = icmp_header(ip_hdr); icmp_hdr_ptr->icmp_sum = 0; icmp_hdr_ptr->icmp_type = type_echo_reply; icmp_hdr_ptr->icmp_code = code_echo_reply; icmp_len = ntohs(ip_hdr->ip_len)-ip_hdr->ip_hl * 4; /* Copy the packet over */ total_len = ip_hdr->ip_len; cache_packet = malloc(total_len); memcpy(cache_packet, ip_hdr, total_len); icmp_hdr_ptr = icmp_header((struct sr_ip_hdr *)cache_packet); icmp_hdr_ptr->icmp_sum = cksum(icmp_hdr_ptr, icmp_len); struct sr_ip_hdr *ip_hdr_csum = (struct sr_ip_hdr *)cache_packet; ip_hdr_csum->ip_sum = cksum(ip_hdr_csum, sizeof(sr_ip_hdr_t)); /* Check if we should send immediately or wait */ arp_entry = sr_arpcache_lookup(&sr->cache, dst); if (arp_entry != 0){ /* Entry Exists, we can send it out right now */ sr_add_ethernet_send(sr, cache_packet, total_len, dst, ethertype_ip); printf("** ARP entry exists, Echo reply sent\n"); } else { req = sr_arpcache_queuereq(&sr->cache, dst, cache_packet, total_len, interface); printf("** ARP entry doesn't exist, Echo reply queued\n"); sr_handle_arpreq(sr, req); } } else if (ip_hdr->ip_p == ip_protocol_tcp||ip_hdr->ip_p == ip_protocol_udp) { /* Send ICMP port unreachable */ icmp_type = 3; icmp_code = 3; sr_icmp_with_payload(sr, ip_hdr, interface, icmp_type, icmp_code); } }
void sr_handle_arpreq(struct sr_instance *sr, struct sr_arpreq *req) { if (difftime(time(0), req->sent) > 0.9) { /* Host is not reachable */ if (req->times_sent >= 5) { printf("** Host Unreachable\n"); /* Send ICMP host unreachable*/ struct sr_packet *ip_packet, *next; ip_packet = req->packets; if(ip_packet != 0){ next = ip_packet->next; } while(ip_packet != 0){ sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(ip_packet->buf); struct sr_if *s_interface = sr_get_interface(sr, ip_packet->iface); uint32_t dst; /* Send ICMP host unreachable */ struct sr_ip_hdr send_ip_hdr; send_ip_hdr.ip_hl = 5; send_ip_hdr.ip_v = ip_hdr->ip_v; send_ip_hdr.ip_tos = 0; send_ip_hdr.ip_id = 0; send_ip_hdr.ip_off = htons(IP_DF); send_ip_hdr.ip_ttl = 100; send_ip_hdr.ip_p = ip_protocol_icmp; send_ip_hdr.ip_sum = 0; send_ip_hdr.ip_dst = ip_hdr->ip_src; send_ip_hdr.ip_src = s_interface->ip; dst = ip_hdr->ip_src; /* Copy the packet over */ uint8_t *cache_packet; uint16_t total_len; uint16_t icmp_len; icmp_len = sizeof(struct sr_icmp_t3_hdr); total_len = ICMP_IP_HDR_LEN_BYTE + icmp_len; send_ip_hdr.ip_len = htons(total_len); send_ip_hdr.ip_sum = cksum(&send_ip_hdr, ICMP_IP_HDR_LEN_BYTE); cache_packet = malloc(total_len); struct sr_icmp_t3_hdr icmp_error_packet = icmp_send_error_packet(ip_hdr, code_host_unreach); memcpy(cache_packet, &(send_ip_hdr), ICMP_IP_HDR_LEN_BYTE); memcpy(cache_packet + ICMP_IP_HDR_LEN_BYTE, &(icmp_error_packet), sizeof(struct sr_icmp_t3_hdr)); print_hdr_ip(cache_packet); struct sr_arpreq *icmp_req; struct sr_arpentry *arp_entry; /* Check ARP cache */ arp_entry = sr_arpcache_lookup(&sr->cache, dst); if (arp_entry != 0){ /* Entry exists, we can send it out right now */ sr_add_ethernet_send(sr, cache_packet, total_len, dst, ethertype_ip); } else { /* Get the interface at which the original packet arrived */ struct sr_rt *lpmatch; struct sr_if *r_iface; lpmatch = longest_prefix_matching(sr, dst); r_iface = sr_get_interface(sr, lpmatch->interface); icmp_req = sr_arpcache_queuereq(&sr->cache, dst, cache_packet, total_len, r_iface->name); sr_handle_arpreq(sr, icmp_req); } ip_packet = next; if(ip_packet != 0){ next = ip_packet->next; } else { sr_arpreq_destroy(&sr->cache, req); } } } else { arp_boardcast(sr, req); printf("** APR boardcasted\n"); req->sent = time(0); req->times_sent ++; } } }
/* * TCP input routine, follows pages 65-76 of the * protocol specification dated September, 1981 very closely. */ void tcp_input(struct mbuf *m, int iphlen, struct socket *inso) { struct ip save_ip, *ip; register struct tcpiphdr *ti; caddr_t optp = NULL; int optlen = 0; int len, tlen, off; register struct tcpcb *tp = NULL; register int tiflags; struct socket *so = NULL; int todrop, acked, ourfinisacked, needoutput = 0; int iss = 0; u_long tiwin; int ret; struct ex_list *ex_ptr; Slirp *slirp; DEBUG_CALL("tcp_input"); DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n", (long )m, iphlen, (long )inso )); /* * If called with m == 0, then we're continuing the connect */ if (m == NULL) { so = inso; slirp = so->slirp; /* Re-set a few variables */ tp = sototcpcb(so); m = so->so_m; so->so_m = NULL; ti = so->so_ti; tiwin = ti->ti_win; tiflags = ti->ti_flags; goto cont_conn; } slirp = m->slirp; /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. */ ti = mtod(m, struct tcpiphdr *); if (iphlen > sizeof(struct ip )) { ip_stripoptions(m, (struct mbuf *)0); iphlen=sizeof(struct ip ); } /* XXX Check if too short */ /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ ip=mtod(m, struct ip *); save_ip = *ip; save_ip.ip_len+= iphlen; /* * Checksum extended TCP header and data. */ tlen = ((struct ip *)ti)->ip_len; tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); ti->ti_x1 = 0; ti->ti_len = htons((u_int16_t)tlen); len = sizeof(struct ip ) + tlen; if(cksum(m, len)) { goto drop; } /* * Check that TCP offset makes sense, * pull out TCP options and adjust length. XXX */ off = ti->ti_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { goto drop; } tlen -= off; ti->ti_len = tlen; if (off > sizeof (struct tcphdr)) { optlen = off - sizeof (struct tcphdr); optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr); } tiflags = ti->ti_flags; /* * Convert TCP protocol specific fields to host format. */ NTOHL(ti->ti_seq); NTOHL(ti->ti_ack); NTOHS(ti->ti_win); NTOHS(ti->ti_urp); /* * Drop TCP, IP headers and TCP options. */ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); if (slirp->restricted) { for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if (ex_ptr->ex_fport == ti->ti_dport && ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) { break; } } if (!ex_ptr) goto drop; } /* * Locate pcb for segment. */ findso: so = slirp->tcp_last_so; if (so->so_fport != ti->ti_dport || so->so_lport != ti->ti_sport || so->so_laddr.s_addr != ti->ti_src.s_addr || so->so_faddr.s_addr != ti->ti_dst.s_addr) { so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport); if (so) slirp->tcp_last_so = so; } /* * If the state is CLOSED (i.e., TCB does not exist) then * all data in the incoming segment is discarded. * If the TCB exists but is in CLOSED state, it is embryonic, * but should either do a listen or a connect soon. * * state == CLOSED means we've done socreate() but haven't * attached it to a protocol yet... * * XXX If a TCB does not exist, and the TH_SYN flag is * the only flag set, then create a session, mark it * as if it was LISTENING, and continue... */ if (so == NULL) { if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN) goto dropwithreset; if ((so = socreate(slirp)) == NULL) goto dropwithreset; if (tcp_attach(so) < 0) { free(so); /* Not sofree (if it failed, it's not insqued) */ goto dropwithreset; } sbreserve(&so->so_snd, TCP_SNDSPACE); sbreserve(&so->so_rcv, TCP_RCVSPACE); so->so_laddr = ti->ti_src; so->so_lport = ti->ti_sport; so->so_faddr = ti->ti_dst; so->so_fport = ti->ti_dport; if ((so->so_iptos = tcp_tos(so)) == 0) so->so_iptos = ((struct ip *)ti)->ip_tos; tp = sototcpcb(so); tp->t_state = TCPS_LISTEN; } /* * If this is a still-connecting socket, this probably * a retransmit of the SYN. Whether it's a retransmit SYN * or something else, we nuke it. */ if (so->so_state & SS_ISFCONNECTING) goto drop; tp = sototcpcb(so); /* XXX Should never fail */ if (tp == NULL) goto dropwithreset; if (tp->t_state == TCPS_CLOSED) goto drop; tiwin = ti->ti_win; /* * Segment received on connection. * Reset idle time and keep-alive timer. */ tp->t_idle = 0; if (SO_OPTIONS) tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; else tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; /* * Process options if not in LISTEN state, * else do it below (after getting remote address). */ if (optp && tp->t_state != TCPS_LISTEN) tcp_dooptions(tp, (u_char *)optp, optlen, ti); /* * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has * no control flags, is in-sequence, the window didn't * change and we're not retransmitting, it's a * candidate. If the length is zero and the ack moved * forward, we're the sender side of the xfer. Just * free the data acked & wake any higher level process * that was blocked waiting for space. If the length * is non-zero and the ack didn't move, we're the * receiver side. If we're getting packets in-order * (the reassembly queue is empty), add the data to * the socket buffer and note that we need a delayed ack. * * XXX Some of these tests are not needed * eg: the tiwin == tp->snd_wnd prevents many more * predictions.. with no *real* advantage.. */ if (tp->t_state == TCPS_ESTABLISHED && (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && ti->ti_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { if (ti->ti_len == 0) { if (SEQ_GT(ti->ti_ack, tp->snd_una) && SEQ_LEQ(ti->ti_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd) { /* * this is a pure ack for outstanding data. */ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) tcp_xmit_timer(tp, tp->t_rtt); acked = ti->ti_ack - tp->snd_una; sbdrop(&so->so_snd, acked); tp->snd_una = ti->ti_ack; m_freem(m); /* * If all outstanding data are acked, stop * retransmit timer, otherwise restart timer * using current (possibly backed-off) value. * If process is waiting for space, * wakeup/selwakeup/signal. If data * are ready to send, let tcp_output * decide between more output or persist. */ if (tp->snd_una == tp->snd_max) tp->t_timer[TCPT_REXMT] = 0; else if (tp->t_timer[TCPT_PERSIST] == 0) tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; /* * This is called because sowwakeup might have * put data into so_snd. Since we don't so sowwakeup, * we don't need this.. XXX??? */ if (so->so_snd.sb_cc) (void) tcp_output(tp); return; } } else if (ti->ti_ack == tp->snd_una && tcpfrag_list_empty(tp) && ti->ti_len <= sbspace(&so->so_rcv)) { /* * this is a pure, in-sequence data packet * with nothing on the reassembly queue and * we have enough buffer space to take it. */ tp->rcv_nxt += ti->ti_len; /* * Add data to socket buffer. */ if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); } else sbappend(so, m); /* * If this is a short packet, then ACK now - with Nagel * congestion avoidance sender won't send more until * he gets an ACK. * * It is better to not delay acks at all to maximize * TCP throughput. See RFC 2581. */ tp->t_flags |= TF_ACKNOW; tcp_output(tp); return; } } /* header prediction */ /* * Calculate amount of space in receive window, * and then do TCP input processing. * Receive window is amount of space in rcv queue, * but not less than advertised window. */ { int win; win = sbspace(&so->so_rcv); if (win < 0) win = 0; tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt)); } switch (tp->t_state) { /* * If the state is LISTEN then ignore segment if it contains an RST. * If the segment contains an ACK then it is bad and send a RST. * If it does not contain a SYN then it is not interesting; drop it. * Don't bother responding if the destination was a broadcast. * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial * tp->iss, and send a segment: * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. * Fill in remote peer address fields if not previously specified. * Enter SYN_RECEIVED state, and process any other fields of this * segment in this state. */ case TCPS_LISTEN: { if (tiflags & TH_RST) goto drop; if (tiflags & TH_ACK) goto dropwithreset; if ((tiflags & TH_SYN) == 0) goto drop; /* * This has way too many gotos... * But a bit of spaghetti code never hurt anybody :) */ /* * If this is destined for the control address, then flag to * tcp_ctl once connected, otherwise connect */ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == slirp->vnetwork_addr.s_addr) { if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr && so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) { /* May be an add exec */ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if(ex_ptr->ex_fport == so->so_fport && so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { so->so_state |= SS_CTL; break; } } if (so->so_state & SS_CTL) { goto cont_input; } } /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */ } if (so->so_emu & EMU_NOCONNECT) { so->so_emu &= ~EMU_NOCONNECT; goto cont_input; } if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { u_char code=ICMP_UNREACH_NET; DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n", errno,strerror(errno))); if(errno == ECONNREFUSED) { /* ACK the SYN, send RST to refuse the connection */ tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0, TH_RST|TH_ACK); } else { if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; HTONL(ti->ti_seq); /* restore tcp header */ HTONL(ti->ti_ack); HTONS(ti->ti_win); HTONS(ti->ti_urp); m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); *ip=save_ip; icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno)); } tp = tcp_close(tp); m_free(m); } else { /* * Haven't connected yet, save the current mbuf * and ti, and return * XXX Some OS's don't tell us whether the connect() * succeeded or not. So we must time it out. */ so->so_m = m; so->so_ti = ti; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; tp->t_state = TCPS_SYN_RECEIVED; } return; cont_conn: /* m==NULL * Check if the connect succeeded */ if (so->so_state & SS_NOFDREF) { tp = tcp_close(tp); goto dropwithreset; } cont_input: tcp_template(tp); if (optp) tcp_dooptions(tp, (u_char *)optp, optlen, ti); if (iss) tp->iss = iss; else tp->iss = slirp->tcp_iss; slirp->tcp_iss += TCP_ISSINCR/2; tp->irs = ti->ti_seq; tcp_sendseqinit(tp); tcp_rcvseqinit(tp); tp->t_flags |= TF_ACKNOW; tp->t_state = TCPS_SYN_RECEIVED; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; goto trimthenstep6; } /* case TCPS_LISTEN */ /* * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una * if SYN has been acked change to ESTABLISHED else SYN_RCVD state * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: if ((tiflags & TH_ACK) && (SEQ_LEQ(ti->ti_ack, tp->iss) || SEQ_GT(ti->ti_ack, tp->snd_max))) goto dropwithreset; if (tiflags & TH_RST) { if (tiflags & TH_ACK) tp = tcp_drop(tp,0); /* XXX Check t_softerror! */ goto drop; } if ((tiflags & TH_SYN) == 0) goto drop; if (tiflags & TH_ACK) { tp->snd_una = ti->ti_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; } tp->t_timer[TCPT_REXMT] = 0; tp->irs = ti->ti_seq; tcp_rcvseqinit(tp); tp->t_flags |= TF_ACKNOW; if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) { soisfconnected(so); tp->t_state = TCPS_ESTABLISHED; (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); /* * if we didn't have to retransmit the SYN, * use its rtt as our initial srtt & rtt var. */ if (tp->t_rtt) tcp_xmit_timer(tp, tp->t_rtt); } else tp->t_state = TCPS_SYN_RECEIVED; trimthenstep6: /* * Advance ti->ti_seq to correspond to first data byte. * If data, trim to stay within window, * dropping FIN if necessary. */ ti->ti_seq++; if (ti->ti_len > tp->rcv_wnd) { todrop = ti->ti_len - tp->rcv_wnd; m_adj(m, -todrop); ti->ti_len = tp->rcv_wnd; tiflags &= ~TH_FIN; } tp->snd_wl1 = ti->ti_seq - 1; tp->rcv_up = ti->ti_seq; goto step6; } /* switch tp->t_state */ /* * States other than LISTEN or SYN_SENT. * Check that at least some bytes of segment are within * receive window. If segment begins before rcv_nxt, * drop leading data (and SYN); if nothing left, just ack. */ todrop = tp->rcv_nxt - ti->ti_seq; if (todrop > 0) { if (tiflags & TH_SYN) { tiflags &= ~TH_SYN; ti->ti_seq++; if (ti->ti_urp > 1) ti->ti_urp--; else tiflags &= ~TH_URG; todrop--; } /* * Following if statement from Stevens, vol. 2, p. 960. */ if (todrop > ti->ti_len || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the window. * At this point the FIN must be a duplicate or out * of sequence; drop it. */ tiflags &= ~TH_FIN; /* * Send an ACK to resynchronize and drop any data. * But keep on processing for RST or ACK. */ tp->t_flags |= TF_ACKNOW; todrop = ti->ti_len; } m_adj(m, todrop); ti->ti_seq += todrop; ti->ti_len -= todrop; if (ti->ti_urp > todrop) ti->ti_urp -= todrop; else { tiflags &= ~TH_URG; ti->ti_urp = 0; } } /* * If new data are received on a connection after the * user processes are gone, then RST the other end. */ if ((so->so_state & SS_NOFDREF) && tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { tp = tcp_close(tp); goto dropwithreset; } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); if (todrop > 0) { if (todrop >= ti->ti_len) { /* * If a new connection request is received * while in TIME_WAIT, drop the old connection * and start over if the sequence numbers * are above the previous ones. */ if (tiflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { iss = tp->rcv_nxt + TCP_ISSINCR; tp = tcp_close(tp); goto findso; } /* * If window is closed can only take segments at * window edge, and have to drop data and PUSH from * incoming segments. Continue processing, but * remember to ack. Otherwise, drop segment * and ack. */ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { tp->t_flags |= TF_ACKNOW; } else { goto dropafterack; } } m_adj(m, -todrop); ti->ti_len -= todrop; tiflags &= ~(TH_PUSH|TH_FIN); } /* * If the RST bit is set examine the state: * SYN_RECEIVED STATE: * If passive open, return to LISTEN state. * If active open, inform user that connection was refused. * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: * Inform user that connection was reset, and close tcb. * CLOSING, LAST_ACK, TIME_WAIT STATES * Close the tcb. */ if (tiflags&TH_RST) switch (tp->t_state) { case TCPS_SYN_RECEIVED: case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: tp->t_state = TCPS_CLOSED; tp = tcp_close(tp); goto drop; case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: tp = tcp_close(tp); goto drop; } /* * If a SYN is in the window, then this is an * error and we send an RST and drop the connection. */ if (tiflags & TH_SYN) { tp = tcp_drop(tp,0); goto dropwithreset; } /* * If the ACK bit is off we drop the segment and return. */ if ((tiflags & TH_ACK) == 0) goto drop; /* * Ack processing. */ switch (tp->t_state) { /* * In SYN_RECEIVED state if the ack ACKs our SYN then enter * ESTABLISHED state and continue processing, otherwise * send an RST. una<=ack<=max */ case TCPS_SYN_RECEIVED: if (SEQ_GT(tp->snd_una, ti->ti_ack) || SEQ_GT(ti->ti_ack, tp->snd_max)) goto dropwithreset; tp->t_state = TCPS_ESTABLISHED; /* * The sent SYN is ack'ed with our sequence number +1 * The first data byte already in the buffer will get * lost if no correction is made. This is only needed for * SS_CTL since the buffer is empty otherwise. * tp->snd_una++; or: */ tp->snd_una=ti->ti_ack; if (so->so_state & SS_CTL) { /* So tcp_ctl reports the right state */ ret = tcp_ctl(so); if (ret == 1) { soisfconnected(so); so->so_state &= ~SS_CTL; /* success XXX */ } else if (ret == 2) { so->so_state &= SS_PERSISTENT_MASK; so->so_state |= SS_NOFDREF; /* CTL_CMD */ } else { needoutput = 1; tp->t_state = TCPS_FIN_WAIT_1; } } else { soisfconnected(so); } (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); tp->snd_wl1 = ti->ti_seq - 1; /* Avoid ack processing; snd_una==ti_ack => dup ack */ goto synrx_to_est; /* fall into ... */ /* * In ESTABLISHED state: drop duplicate ACKs; ACK out of range * ACKs. If the ack is in the range * tp->snd_una < ti->ti_ack <= tp->snd_max * then advance tp->snd_una to ti->ti_ack and drop * data from the retransmission queue. If this ACK reflects * more up to date window information we update our window information. */ case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n", (long )m, (long )so)); /* * If we have outstanding data (other than * a window probe), this is a completely * duplicate ack (ie, window info didn't * change), the ack is the biggest we've * seen and we've seen exactly our rexmt * threshold of them, assume a packet * has been dropped and retransmit it. * Kludge snd_nxt & the congestion * window so we send only this one * packet. * * We know we're losing at the current * window size so do congestion avoidance * (set ssthresh to half the current window * and pull our congestion window back to * the new ssthresh). * * Dup acks mean that packets have left the * network (they're now cached at the receiver) * so bump cwnd by the amount in the receiver * to keep a constant cwnd packets in the * network. */ if (tp->t_timer[TCPT_REXMT] == 0 || ti->ti_ack != tp->snd_una) tp->t_dupacks = 0; else if (++tp->t_dupacks == TCPREXMTTHRESH) { tcp_seq onxt = tp->snd_nxt; u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; if (win < 2) win = 2; tp->snd_ssthresh = win * tp->t_maxseg; tp->t_timer[TCPT_REXMT] = 0; tp->t_rtt = 0; tp->snd_nxt = ti->ti_ack; tp->snd_cwnd = tp->t_maxseg; (void) tcp_output(tp); tp->snd_cwnd = tp->snd_ssthresh + tp->t_maxseg * tp->t_dupacks; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; goto drop; } else if (tp->t_dupacks > TCPREXMTTHRESH) { tp->snd_cwnd += tp->t_maxseg; (void) tcp_output(tp); goto drop; } } else tp->t_dupacks = 0; break; } synrx_to_est: /* * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ if (tp->t_dupacks > TCPREXMTTHRESH && tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; if (SEQ_GT(ti->ti_ack, tp->snd_max)) { goto dropafterack; } acked = ti->ti_ack - tp->snd_una; /* * If transmit timer is running and timed sequence * number was acked, update smoothed round trip time. * Since we now have an rtt measurement, cancel the * timer backoff (cf., Phil Karn's retransmit alg.). * Recompute the initial retransmit timer. */ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) tcp_xmit_timer(tp,tp->t_rtt); /* * If all outstanding data is acked, stop retransmit * timer and remember to restart (more output or persist). * If there is more data to be acked, restart retransmit * timer, using current (possibly backed-off) value. */ if (ti->ti_ack == tp->snd_max) { tp->t_timer[TCPT_REXMT] = 0; needoutput = 1; } else if (tp->t_timer[TCPT_PERSIST] == 0) tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; /* * When new data is acked, open the congestion window. * If the window gives us less than ssthresh packets * in flight, open exponentially (maxseg per packet). * Otherwise open linearly: maxseg per window * (maxseg^2 / cwnd per packet). */ { register u_int cw = tp->snd_cwnd; register u_int incr = tp->t_maxseg; if (cw > tp->snd_ssthresh) incr = incr * incr / cw; tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale); } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; sbdrop(&so->so_snd, (int )so->so_snd.sb_cc); ourfinisacked = 1; } else { sbdrop(&so->so_snd, acked); tp->snd_wnd -= acked; ourfinisacked = 0; } tp->snd_una = ti->ti_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; switch (tp->t_state) { /* * In FIN_WAIT_1 STATE in addition to the processing * for the ESTABLISHED state if our FIN is now acknowledged * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: if (ourfinisacked) { /* * If we can't receive any more * data, then closing user can proceed. * Starting the timer is contrary to the * specification, but if we don't get a FIN * we'll hang forever. */ if (so->so_state & SS_FCANTRCVMORE) { tp->t_timer[TCPT_2MSL] = TCP_MAXIDLE; } tp->t_state = TCPS_FIN_WAIT_2; } break; /* * In CLOSING STATE in addition to the processing for * the ESTABLISHED state if the ACK acknowledges our FIN * then enter the TIME-WAIT state, otherwise ignore * the segment. */ case TCPS_CLOSING: if (ourfinisacked) { tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; } break; /* * In LAST_ACK, we may still be waiting for data to drain * and/or to be acked, as well as for the ack of our FIN. * If our FIN is now acknowledged, delete the TCB, * enter the closed state and return. */ case TCPS_LAST_ACK: if (ourfinisacked) { tp = tcp_close(tp); goto drop; } break; /* * In TIME_WAIT state the only thing that should arrive * is a retransmission of the remote FIN. Acknowledge * it and restart the finack timer. */ case TCPS_TIME_WAIT: tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; goto dropafterack; } } /* switch(tp->t_state) */ step6: /* * Update window information. * Don't look at window if no ACK: TAC's send garbage on first SYN. */ if ((tiflags & TH_ACK) && (SEQ_LT(tp->snd_wl1, ti->ti_seq) || (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) || (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) { tp->snd_wnd = tiwin; tp->snd_wl1 = ti->ti_seq; tp->snd_wl2 = ti->ti_ack; if (tp->snd_wnd > tp->max_sndwnd) tp->max_sndwnd = tp->snd_wnd; needoutput = 1; } /* * Process segments with URG. */ if ((tiflags & TH_URG) && ti->ti_urp && TCPS_HAVERCVDFIN(tp->t_state) == 0) { /* * This is a kludge, but if we receive and accept * random urgent pointers, we'll crash in * soreceive. It's hard to imagine someone * actually wanting to send this much urgent data. */ if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) { ti->ti_urp = 0; tiflags &= ~TH_URG; goto dodata; } /* * If this segment advances the known urgent pointer, * then mark the data stream. This should not happen * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since * a FIN has been received from the remote side. * In these states we ignore the URG. * * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section as the original * spec states (in one of two places). */ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { tp->rcv_up = ti->ti_seq + ti->ti_urp; so->so_urgc = so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt); /* -1; */ tp->rcv_up = ti->ti_seq + ti->ti_urp; } } else /* * If no out of band data is expected, * pull receive urgent pointer along * with the receive window. */ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) tp->rcv_up = tp->rcv_nxt; dodata: /* * Process the segment text, merging it into the TCP sequencing queue, * and arranging for acknowledgment of receipt if necessary. * This process logically involves adjusting tp->rcv_wnd as data * is presented to the user (this happens in tcp_usrreq.c, * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ if ((ti->ti_len || (tiflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { TCP_REASS(tp, ti, m, so, tiflags); /* * Note the amount of data that peer has sent into * our window, in order to estimate the sender's * buffer size. */ len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt); } else { m_free(m); tiflags &= ~TH_FIN; } /* * If FIN is received ACK the FIN and let the user know * that the connection is closing. */ if (tiflags & TH_FIN) { if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { /* * If we receive a FIN we can't send more data, * set it SS_FDRAIN * Shutdown the socket if there is no rx data in the * buffer. * soread() is called on completion of shutdown() and * will got to TCPS_LAST_ACK, and use tcp_output() * to send the FIN. */ sofwdrain(so); tp->t_flags |= TF_ACKNOW; tp->rcv_nxt++; } switch (tp->t_state) { /* * In SYN_RECEIVED and ESTABLISHED STATES * enter the CLOSE_WAIT state. */ case TCPS_SYN_RECEIVED: case TCPS_ESTABLISHED: if(so->so_emu == EMU_CTL) /* no shutdown on socket */ tp->t_state = TCPS_LAST_ACK; else tp->t_state = TCPS_CLOSE_WAIT; break; /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. */ case TCPS_FIN_WAIT_1: tp->t_state = TCPS_CLOSING; break; /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. */ case TCPS_FIN_WAIT_2: tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; break; /* * In TIME_WAIT state restart the 2 MSL time_wait timer. */ case TCPS_TIME_WAIT: tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; break; } } /* * If this is a small packet, then ACK now - with Nagel * congestion avoidance sender won't send more until * he gets an ACK. * * See above. */ if (ti->ti_len && (unsigned)ti->ti_len <= 5 && ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { tp->t_flags |= TF_ACKNOW; } /* * Return any desired output. */ if (needoutput || (tp->t_flags & TF_ACKNOW)) { (void) tcp_output(tp); } return; dropafterack: /* * Generate an ACK dropping incoming segment if it occupies * sequence space, where the ACK reflects our state. */ if (tiflags & TH_RST) goto drop; m_freem(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; dropwithreset: /* reuses m if m!=NULL, m_free() unnecessary */ if (tiflags & TH_ACK) tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST); else { if (tiflags & TH_SYN) ti->ti_len++; tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0, TH_RST|TH_ACK); } return; drop: /* * Drop space held by incoming segment and return. */ m_free(m); return; }
void icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message) { unsigned hlen, shlen, s_ip_len; register struct ip *ip; register struct icmp *icp; register struct mbuf *m; DEBUG_CALL("icmp_send_error"); DEBUG_ARG("msrc = %p", msrc); DEBUG_ARG("msrc_len = %d", msrc->m_len); if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error; /* check msrc */ if(!msrc) goto end_error; ip = mtod(msrc, struct ip *); #ifdef DEBUG { char bufa[20], bufb[20]; strcpy(bufa, inet_ntoa(ip->ip_src)); strcpy(bufb, inet_ntoa(ip->ip_dst)); DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb)); } #endif if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ /* Do not reply to source-only IPs */ if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) { goto end_error; } shlen=ip->ip_hl << 2; s_ip_len=ip->ip_len; if(ip->ip_p == IPPROTO_ICMP) { icp = (struct icmp *)((char *)ip + shlen); /* * Assume any unknown ICMP type is an error. This isn't * specified by the RFC, but think about it.. */ if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error; } /* make a copy */ m = m_get(msrc->slirp); if (!m) { goto end_error; } { int new_m_size; new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN; if(new_m_size>m->m_size) m_inc(m, new_m_size); } memcpy(m->m_data, msrc->m_data, msrc->m_len); m->m_len = msrc->m_len; /* copy msrc to m */ /* make the header of the reply packet */ ip = mtod(m, struct ip *); hlen= sizeof(struct ip ); /* no options in reply */ /* fill in icmp */ m->m_data += hlen; m->m_len -= hlen; icp = mtod(m, struct icmp *); if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */ else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */ s_ip_len=ICMP_MAXDATALEN; m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */ /* min. size = 8+sizeof(struct ip)+8 */ icp->icmp_type = type; icp->icmp_code = code; icp->icmp_id = 0; icp->icmp_seq = 0; memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */ HTONS(icp->icmp_ip.ip_len); HTONS(icp->icmp_ip.ip_id); HTONS(icp->icmp_ip.ip_off); #ifdef DEBUG if(message) { /* DEBUG : append message to ICMP packet */ int message_len; char *cpnt; message_len=strlen(message); if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN; cpnt=(char *)m->m_data+m->m_len; memcpy(cpnt, message, message_len); m->m_len+=message_len; } #endif icp->icmp_cksum = 0; icp->icmp_cksum = cksum(m, m->m_len); m->m_data -= hlen; m->m_len += hlen; /* fill in ip */ ip->ip_hl = hlen >> 2; ip->ip_len = m->m_len; ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */ ip->ip_ttl = MAXTTL; ip->ip_p = IPPROTO_ICMP; ip->ip_dst = ip->ip_src; /* ip addresses */ ip->ip_src = m->slirp->vhost_addr; (void ) ip_output((struct socket *)NULL, m); end_error: return; }
int handle_ip_pkt(struct sr_instance* sr, uint8_t* Pkt, char* interface, unsigned int len, uint8_t srcMAC[]) { //actually I have to pass in a uint8_t* packet not an sr_ip_pkt... /* Extract the IP header Header actions: 1) check the IP checksum -> discard if wrong 2) check if TTL expired -> send ICMP type 11 (0) 3) check if it is our message -> if yes jump to "OURS:" Not Ours: 1) Find the next hop in rtable 2) Decrement TTL, recalculate Checksum 3) Assemble new packet 4) Send on correct interface OURS: 1) Check type of packet -> ICMP -> UDP/TCP ICMP: 1) Check type 2) respond as appropriate UDP/TCP 1) Pass up the stack */ assert(Pkt); assert(sr); assert(interface); //struct sr_router* subsystem = (struct sr_router*)sr_get_subsystem(sr); struct sr_vns_if* myIntf = sr_get_interface(sr, interface); uint32_t myIP = myIntf->ip; struct ip* pktHdr = (struct ip*)Pkt; uint8_t* payload = (uint8_t*)malloc_or_die(len - 20); uint8_t* ptr = Pkt + 20; memcpy(payload, ptr, len-20); uint32_t originIP = pktHdr->ip_src.s_addr; //first thing is to update ARP - no point wasiting useful information is there! time_t now = time(NULL); int checkArp; checkArp = find_and_update(sr, originIP, srcMAC, now); if(pktHdr->ip_hl != 5) { //means there were IP options so unerachable (host or network?) printf("@@@@@@@@@@@@@@@@@@@@@@@@@@ IP hdr length was %u. Should have been 5 (e.g. 20 bytes)\n", pktHdr->ip_hl); uint8_t* data = (uint8_t*)malloc_or_die(28); memcpy(data, Pkt, 28); int tmp; tmp = create_ICMP_pkt(sr, interface, originIP, 3, 1, 0, 0, data, 28); return -3; } if(cksum((uint8_t*)pktHdr, 20)) { printf("@@@@@@@@@@@@@@@@@@@@@@ CHECKSUM 0x%hx WAS INVALID SO WE WILL DROP THE PACKET\n", pktHdr->ip_sum); return -2; } if(pktHdr->ip_ttl < 2) { printf("@@@@@@@@@@@@@@@@@@@@@@ TTL has expired so we will send back an ICMP\n"); //get the data (IP hdr + 8 bytes of packet) uint8_t* data = (uint8_t*)malloc_or_die(28); memcpy(data, Pkt, 28); int tmp; tmp = create_ICMP_pkt(sr, interface, originIP, 11, 0, 0, 0, data, 28); return -1; } if(check_my_interface(sr, (uint32_t)pktHdr->ip_dst.s_addr)) { // printf("This is my packet so I will process it\n"); switch(pktHdr->ip_p) { case IPPROTO_ICMP: ; //printf("\nThis is an IP ICMP packet of length %d\n", len); // printf("After removing the IP header we are left with %d bytes\n", datalen); int datalen = len - sizeof(struct ip); uint8_t* ICMP = (uint8_t*) malloc_or_die(datalen); //ICMP = array_cpy(Pkt->payload, datalen); memcpy(ICMP, payload, datalen); int tmp; tmp = process_ICMP_pkt(sr, interface, originIP, ICMP, datalen); return tmp; case IPPROTO_TCP: printf("TCP packet sent up the stack\n"); sr_transport_input(Pkt); return 1; default: return 0; } } else { //printf("********** Not my packet, so I will forward it\n"); return 3; } return 0; }
/* m->m_data points at ip packet header * m->m_len length ip packet * ip->ip_len length data (IPDU) */ void udp_input(register struct mbuf *m, int iphlen) { Slirp *slirp = m->slirp; register struct ip *ip; register struct udphdr *uh; int len; struct ip save_ip; struct socket *so; DEBUG_CALL("udp_input"); DEBUG_ARG("m = %lx", (long)m); DEBUG_ARG("iphlen = %d", iphlen); /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, * but we don't yet have a way to check the checksum * with options still present. */ if(iphlen > sizeof(struct ip)) { ip_stripoptions(m, (struct mbuf *)0); iphlen = sizeof(struct ip); } /* * Get IP and UDP header together in first mbuf. */ ip = mtod(m, struct ip *); uh = (struct udphdr *)((caddr_t)ip + iphlen); /* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = ntohs((uint16_t)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { goto bad; } m_adj(m, len - ip->ip_len); ip->ip_len = len; } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ save_ip = *ip; save_ip.ip_len+= iphlen; /* tcp_input subtracts this */ /* * Checksum extended UDP header and data. */ if (uh->uh_sum) { memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr)); ((struct ipovly *)ip)->ih_x1 = 0; ((struct ipovly *)ip)->ih_len = uh->uh_ulen; if(cksum(m, len + sizeof(struct ip))) { goto bad; } } /* * handle DHCP/BOOTP */ if (ntohs(uh->uh_dport) == BOOTP_SERVER && (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || ip->ip_dst.s_addr == 0xffffffff)) { bootp_input(m); goto bad; } /* * handle TFTP */ if (ntohs(uh->uh_dport) == TFTP_SERVER && ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { tftp_input(m); goto bad; } if (slirp->restricted) { goto bad; } /* * Locate pcb for datagram. */ so = slirp->udp_last_so; if (so->so_lport != uh->uh_sport || so->so_laddr.s_addr != ip->ip_src.s_addr) { struct socket *tmp; for (tmp = slirp->udb.so_next; tmp != &slirp->udb; tmp = tmp->so_next) { if (tmp->so_lport == uh->uh_sport && tmp->so_laddr.s_addr == ip->ip_src.s_addr) { so = tmp; break; } } if (tmp == &slirp->udb) { so = NULL; } else { slirp->udp_last_so = so; } } if (so == NULL) { /* * If there's no socket for this packet, * create one */ so = socreate(slirp); if (!so) { goto bad; } if(udp_attach(so) == -1) { DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); goto bad; } /* * Setup fields */ so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; if ((so->so_iptos = udp_tos(so)) == 0) so->so_iptos = ip->ip_tos; /* * XXXXX Here, check if it's in udpexec_list, * and if it is, do the fork_exec() etc. */ } so->so_faddr = ip->ip_dst; /* XXX */ so->so_fport = uh->uh_dport; /* XXX */ iphlen += sizeof(struct udphdr); m->m_len -= iphlen; m->m_data += iphlen; /* * Now we sendto() the packet. */ if(sosendto(so,m) == -1) { m->m_len += iphlen; m->m_data -= iphlen; *ip=save_ip; DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); } m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ /* restore the orig mbuf packet */ m->m_len += iphlen; m->m_data -= iphlen; *ip=save_ip; so->so_m=m; /* ICMP backup */ return; bad: m_free(m); }
int create_ICMP_pkt(struct sr_instance* sr, char* interface, uint32_t dstIP, uint8_t type, uint8_t code, uint16_t field1, uint16_t field2, uint8_t* data, unsigned int datalen) { assert(data); assert(sr); assert(interface); #if 0 printf("********* at %s Payload is 0x ", __func__); int i; for(i = 0; i < datalen; i++) { printf("%x", data[i]); } printf("\n"); #endif int len, rtn; switch (type) { case 3: case 11: len = 8 + sizeof(struct ip) + 8; //8byte ICMP hdr, 20byte IP hdr, 8byte IP data break; //for now to keep it easy to follow code case 0: len = sizeof(struct sr_icmp_hdr) + datalen; // 8byte ICMP hdr, data as in echo rqst allowing for ID and sequence in data break; default: len = 8; //this might not be helpful... } //printf("******** We're assigning %d bytes of memory\n", len); struct sr_icmp_hdr* packet = (struct sr_icmp_hdr*) malloc_or_die(sizeof(struct sr_icmp_hdr)); // now they all get some std entries packet->type = type; packet->code = code; packet->checksum = 0x0; // needs to be 0'ed before calculation packet->field1 = field1; packet->field2 = field2; //and now we fill the other entries as appropriate // printf("********* at %s After conversion to packet \n ", __func__); // print_icmp_pkt(packet); //finally we can add a checksum //uint16_t check = cksum((uint8_t*)packet, len); // The problem is I can't do this cast apparently //printf("\n\n********* at %s transferring 0x \n", __func__); //print_icmp_pkt(packet); uint8_t* Pkt = array_join(packet, data, sizeof(struct sr_icmp_hdr), datalen); //memcpy(Pkt, packet, len); uint16_t check = cksum(Pkt, len); // printf("\n\n &&&&&&&&&& the checksum is calculated as %hu\n", check); ((struct sr_icmp_hdr*)Pkt)->checksum = check; // Pkt = icmp_to_raw(packet, len); //so we still have the issue of how to insert the checksum rtn = make_and_send(sr, interface, dstIP, Pkt, len, IPPROTO_ICMP); if(rtn == 2) { printf("************* ICMP pkt, ARP request sent\n"); return 2; } else if (rtn == 1) { printf("************** ICMP packet, we had an ARP entry\n"); return 2; } else { return 0; } return 0; }
int make_and_send(struct sr_instance* sr, char * interface, uint32_t dstIP, uint8_t* payld, unsigned int len, uint8_t proto) { assert(payld); assert(sr); assert(interface); #if 0 printf("********* at %s Payload is 0x ", __func__); int i ; for(i = 0; i < len; i++) { printf("%hhx ", payld[i]); } printf("\n"); #endif /* doing some testing */ printf("\n*************************\n\n"); print_ip(dstIP); printf("\n\n**************************\n\n"); char* test; test = longest_prefix(sr, ntohl(dstIP)); printf("\n\n**************************\n"); struct sr_vns_if* myIntf = sr_get_interface(sr, interface); struct ip* ipHdr = (struct ip*) malloc_or_die(20); int rtn; struct in_addr src; struct in_addr dst; src.s_addr = myIntf->ip; dst.s_addr = dstIP; //fill in ip hdr //How do we set version and hl? ipHdr->ip_hl = 5; ipHdr->ip_v = 4; ipHdr->ip_tos = 0x0; ipHdr->ip_len = htons(len + 20); ipHdr->ip_id = htons(1638); ipHdr->ip_off = 0x0; ipHdr->ip_ttl = 64; ipHdr->ip_p = proto; // probably always IPPROTO_ICMP for the router ipHdr->ip_sum = 0x0; ipHdr->ip_src = src; ipHdr->ip_dst = dst; uint16_t check = cksum((uint8_t*)ipHdr, 20); ipHdr->ip_sum = check; //Then we make an ethernet payload // Tracking back from sr_arp.c:298 // we don't have the valid ICMP here even so go back further uint8_t* packet = (uint8_t*) malloc_or_die(len + 20); packet = array_join(ipHdr, payld, 20, len); #if 0 printf("\n"); for(i = 0; i < len + 20; i++) { printf("%hhx ", packet[i]); } //printf("\n************* %s ******************\n", __func__); #endif rtn = arp_lookup(sr, interface, packet, dstIP, (len + 20)); return rtn; }
sizeof(struct egp_hdr) + sizeof(struct egp_acq_hdr)); /* * @nbrito -- Tue Jan 18 11:09:34 BRST 2011 * XXX Have to work a little bit more deeply in packet building. * XXX Checking EGP Type and building appropriate header. */ /* EGP Header structure making a pointer to Packet. */ egp = (struct egp_hdr *)((void *)ip + sizeof(struct iphdr) + greoptlen); egp->version = EGPVERSION; egp->type = co->egp.type; egp->code = co->egp.code; egp->status = co->egp.status; egp->as = __RND(co->egp.as); egp->sequence = __RND(co->egp.sequence); egp->check = 0; /* EGP Acquire Header structure. */ egp_acq = (struct egp_acq_hdr *)((void *)egp + sizeof(struct egp_hdr)); egp_acq->hello = __RND(co->egp.hello); egp_acq->poll = __RND(co->egp.poll); /* Computing the checksum. */ egp->check = co->bogus_csum ? random() : cksum(egp, sizeof(struct egp_hdr) + sizeof(struct egp_acq_hdr)); /* GRE Encapsulation takes place. */ gre_checksum(packet, co, *size); }
gre_ip = gre_encapsulation(packet, co, sizeof(struct iphdr) + sizeof(struct udphdr)); /* UDP Header structure making a pointer to IP Header structure. */ udp = (struct udphdr *)((void *)ip + sizeof(struct iphdr) + greoptlen); udp->source = htons(IPPORT_RND(co->source)); udp->dest = htons(IPPORT_RND(co->dest)); udp->len = htons(sizeof(struct udphdr)); udp->check = 0; /* needed 'cause of cksum(), below! */ /* Fill PSEUDO Header structure. */ pseudo = (struct psdhdr *)((void *)udp + sizeof(struct udphdr)); pseudo->saddr = co->encapsulated ? gre_ip->saddr : ip->saddr; pseudo->daddr = co->encapsulated ? gre_ip->daddr : ip->daddr; pseudo->zero = 0; pseudo->protocol = co->ip.protocol; pseudo->len = htons(sizeof(struct udphdr)); /* Computing the checksum. */ udp->check = co->bogus_csum ? RANDOM() : cksum(udp, sizeof(struct udphdr) + sizeof(struct psdhdr)); #ifdef DUMP_DATA dump_udp(fdebug, udp); dump_psdhdr(fdebug, pseudo); #endif gre_checksum(packet, co, *size); }
/* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */ int ip_output(struct socket *so, struct mbuf *m0) { register struct ip *ip; register struct mbuf *m = m0; register int hlen = sizeof(struct ip ); int len, off, error = 0; DEBUG_CALL("ip_output"); DEBUG_ARG("so = %lx", (long)so); DEBUG_ARG("m0 = %lx", (long)m0); /* We do no options */ /* if (opt) { * m = ip_insertoptions(m, opt, &len); * hlen = len; * } */ ip = mtod(m, struct ip *); /* * Fill in IP header. */ ip->ip_v = IPVERSION; ip->ip_off &= IP_DF; ip->ip_id = htons(ip_id++); ip->ip_hl = hlen >> 2; STAT(ipstat.ips_localout++); /* * Verify that we have any chance at all of being able to queue * the packet or packet fragments */ /* XXX Hmmm... */ /* if (if_queued > IF_THRESH && towrite <= 0) { * error = ENOBUFS; * goto bad; * } */ /* * If small enough for interface, can just send directly. */ if ((u_int16_t)ip->ip_len <= IF_MTU) { ip->ip_len = htons((u_int16_t)ip->ip_len); ip->ip_off = htons((u_int16_t)ip->ip_off); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); if_output(so, m); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ if (ip->ip_off & IP_DF) { error = -1; STAT(ipstat.ips_cantfrag++); goto bad; } len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */ if (len < 8) { error = -1; goto bad; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_nextpkt; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { register struct ip *mhip; m = m_get(); if (m == NULL) { error = -1; STAT(ipstat.ips_odropped++); goto sendorfree; } m->m_data += IF_MAXLINKHDR; mhip = mtod(m, struct ip *); *mhip = *ip; /* No options */ /* if (hlen > sizeof (struct ip)) { * mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); * mhip->ip_hl = mhlen >> 2; * } */ m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); if (ip->ip_off & IP_MF) mhip->ip_off |= IP_MF; if (off + len >= (u_int16_t)ip->ip_len) len = (u_int16_t)ip->ip_len - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_int16_t)(len + mhlen)); if (m_copy(m, m0, off, len) < 0) { error = -1; goto sendorfree; } mhip->ip_off = htons((u_int16_t)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = cksum(m, mhlen); *mnext = m; mnext = &m->m_nextpkt; STAT(ipstat.ips_ofragments++); } /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m = m0; m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len); ip->ip_len = htons((u_int16_t)m->m_len); ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); sendorfree: for (m = m0; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = NULL; if (error == 0) if_output(so, m); else m_freem(m); } if (error == 0) STAT(ipstat.ips_fragmented++); } done: return (error); bad: m_freem(m0); goto done; }
int handle_ip_packet(struct sr_instance * sr, uint8_t * packet, unsigned int len ) { sr_ip_hdr_t *iphdr = (sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)); /* validate checksum. */ uint16_t checksum; checksum = cksum(iphdr, sizeof(*iphdr)); if (checksum != 0xffff) { return -1; } uint8_t * newpacket_for_ip = (uint8_t *) malloc(len); memcpy(newpacket_for_ip, packet, len); sr_ip_hdr_t *new_iphdr = (sr_ip_hdr_t *)(newpacket_for_ip + sizeof(sr_ethernet_hdr_t)); /* Decrement the TTL by 1, and recompute the packet checksum over the modified header. */ /* decrement ttl */ new_iphdr->ip_ttl--; if (new_iphdr->ip_ttl <= 0) { /* check ttl, less than zero */ send_icmp_message(sr,packet, 11, 0); return -1; } /* update checksum. */ new_iphdr->ip_sum = 0; checksum = cksum(new_iphdr, sizeof(*new_iphdr)); new_iphdr->ip_sum = checksum; checksum = cksum(new_iphdr, sizeof(*new_iphdr)); struct sr_if* assoc_iface = validate_ip(sr->if_list, iphdr->ip_dst); if (assoc_iface) { /*it's destined to one of our IPs */ /* ICMP */ uint8_t ip_proto = ip_protocol(packet + sizeof(sr_ethernet_hdr_t)); if (ip_proto == ip_protocol_icmp) { int minlength = sizeof(sr_icmp_hdr_t) + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t); if (len < minlength){ return -1; } struct sr_icmp_hdr * icmp_hdr = (struct sr_icmp_hdr *) (packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); if(icmp_hdr->icmp_type == 8){ /* is an echo request */ int res; res = make_echo_request(&newpacket_for_ip, len); if (res == -1){ return -1; } } /* end ICMP */ } else { /* got a udp payload to a rounter interface */ int res; res = send_icmp_message(sr, packet, 3, 3); if (res == -1){ return -1; } } } /* Find out which entry in the routing table has the longest prefix match with the destination IP address. */ struct sr_rt* best_rt = find_best_rt(sr->routing_table, ntohl(new_iphdr->ip_dst)); if (!best_rt) { /* didn't find an interface, send an ICMP message type 3 code 0, also if there are any errors above */ int res = send_icmp_message(sr, packet, 3, 0); if (res == -1){ return -1; } return 0; } /* found an interface */ struct sr_if * best_iface = sr_get_interface(sr, best_rt->interface); if (!best_iface){ return -1; } struct sr_arpentry * forward_arp_entry = sr_arpcache_lookup(&(sr->cache), best_rt->gw.s_addr); struct sr_ethernet_hdr * new_ether_hdr = (struct sr_ethernet_hdr * ) newpacket_for_ip; /* ethernet -- update the source address */ memcpy(new_ether_hdr->ether_shost, best_iface->addr, ETHER_ADDR_LEN); if (forward_arp_entry) { /* we have a MAC address */ /* update packet */ /* ethernet -- set the dest address */ memcpy(new_ether_hdr->ether_dhost, forward_arp_entry->mac, ETHER_ADDR_LEN); /* send packet using correct interface */ int res = 0; res = sr_send_packet(sr, newpacket_for_ip, len, best_rt->interface); if (res != 0) { return -1; } free(forward_arp_entry); } else { /* we dont have a MAC address, add to arp queue */ struct sr_arpreq * arpreq; arpreq = sr_arpcache_queuereq(&(sr->cache), best_rt->gw.s_addr, newpacket_for_ip, len, best_rt->interface ); if (!arpreq){ return -1; } uint32_t ip, dest; ip = ntohl(best_iface->ip); dest = ntohl(best_rt->dest.s_addr); sr_handle_arp_req(sr, arpreq); } return 0; }
int putfile(char *from_file, int to) { struct exec ex; register int n, total; char buf[2048]; int from, check_sum = 0; struct lif_load load; if ((from = open(from_file, O_RDONLY)) < 0) err(1, "%s", from_file); n = read(from, &ex, sizeof(ex)); if (n != sizeof(ex)) err(1, "%s: reading file header", from_file); entry = ex.a_entry; if (N_GETMAGIC(ex) == OMAGIC || N_GETMAGIC(ex) == NMAGIC) entry += sizeof(ex); else if (IS_ELF(*(Elf32_Ehdr *)&ex)) { Elf32_Ehdr elf_header; Elf32_Phdr *elf_segments; int i, header_count, memory_needed, elf_load_image_segment; (void) lseek(from, 0, SEEK_SET); n = read(from, &elf_header, sizeof(elf_header)); if (n != sizeof (elf_header)) err(1, "%s: reading ELF header", from_file); header_count = ntohs(elf_header.e_phnum); elf_segments = reallocarray(NULL, header_count, sizeof(*elf_segments)); if (elf_segments == NULL) err(1, "malloc"); memory_needed = header_count * sizeof(*elf_segments); (void) lseek(from, ntohl(elf_header.e_phoff), SEEK_SET); n = read(from, elf_segments, memory_needed); if (n != memory_needed) err(1, "%s: reading ELF segments", from_file); elf_load_image_segment = -1; for (i = 0; i < header_count; i++) { if (elf_segments[i].p_filesz && ntohl(elf_segments[i].p_flags) & PF_X) { if (elf_load_image_segment != -1) errx(1, "%s: more than one ELF program segment", from_file); elf_load_image_segment = i; } } if (elf_load_image_segment == -1) errx(1, "%s: no suitable ELF program segment", from_file); entry = ntohl(elf_header.e_entry) + ntohl(elf_segments[elf_load_image_segment].p_offset) - ntohl(elf_segments[elf_load_image_segment].p_vaddr); free(elf_segments); } else if (*(u_char *)&ex == 0x1f && ((u_char *)&ex)[1] == 0x8b) { entry = 0; } else errx(1, "%s: bad magic number", from_file); entry += sizeof(load); lseek(to, sizeof(load), SEEK_CUR); total = 0; n = sizeof(buf) - sizeof(load); /* copy the whole file */ for (lseek(from, 0, 0); ; n = sizeof(buf)) { bzero(buf, sizeof(buf)); if ((n = read(from, buf, n)) < 0) err(1, "%s", from_file); else if (n == 0) break; if (write(to, buf, n) != n) err(1, "%s", to_file); total += n; check_sum = cksum(check_sum, (int *)buf, n); } /* load header */ load.address = htobe32(loadpoint + sizeof(load)); load.count = htobe32(4 + total); check_sum = cksum(check_sum, (int *)&load, sizeof(load)); if (verbose) warnx("wrote %d bytes of file \'%s\'", total, from_file); total += sizeof(load); /* insert the header */ lseek(to, -total, SEEK_CUR); if (write(to, &load, sizeof(load)) != sizeof(load)) err(1, "%s", to_file); lseek(to, total - sizeof(load), SEEK_CUR); bzero(buf, sizeof(buf)); /* pad to int */ n = sizeof(int) - total % sizeof(int); if (total % sizeof(int)) { if (write(to, buf, n) != n) err(1, "%s", to_file); else total += n; } /* pad to the blocksize */ n = sizeof(buf) - total % sizeof(buf); if (n < sizeof(int)) { n += sizeof(buf); total += sizeof(buf); } else total += n; /* TODO should pad here to the 65k boundary for tape boot */ if (verbose) warnx("checksum is 0x%08x", -check_sum); check_sum = htobe32(-check_sum); if (write(to, &check_sum, sizeof(int)) != sizeof(int)) err(1, "%s", to_file); n -= sizeof(int); if (write(to, buf, n) != n) err(1, "%s", to_file); if (close(from) < 0 ) err(1, "%s", from_file); return total; }
int send_icmp_message(struct sr_instance * sr, uint8_t * packet, uint8_t icmp_type, uint8_t icmp_code) { int res, size, icmp_len; if (icmp_type == 3){ size = sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_hdr_t); } else if (icmp_type == 11) { size = sizeof(sr_ethernet_hdr_t) + 2 * sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_t3_hdr_t) + 8; } else { return -1; } uint8_t temp[ETHER_ADDR_LEN]; uint8_t icmp_message[size]; memcpy(icmp_message, packet, size); icmp_len = size - ( sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); struct sr_ethernet_hdr * ether_hdr = (struct sr_ethernet_hdr * ) icmp_message; struct sr_ip_hdr *ip_hdr = (struct sr_ip_hdr *)(icmp_message + sizeof(sr_ethernet_hdr_t)); struct sr_ip_hdr *old_ip_hdr = (struct sr_ip_hdr *)(packet + sizeof(sr_ethernet_hdr_t)); struct sr_icmp_hdr * icmp_hdr = (struct sr_icmp_hdr *) (icmp_message + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); /* swap source and host MAC address */ memcpy(temp, ether_hdr->ether_dhost, ETHER_ADDR_LEN); memcpy(ether_hdr->ether_dhost, ether_hdr->ether_shost, ETHER_ADDR_LEN); memcpy(ether_hdr->ether_shost, temp, ETHER_ADDR_LEN); if (icmp_type == 11) { struct sr_icmp_t3_hdr * t3_hdr = (struct sr_icmp_t3_hdr *) icmp_hdr; memcpy(t3_hdr->data, old_ip_hdr, ICMP_DATA_SIZE); t3_hdr->next_mtu = 1500; } /* updte icmp info */ uint32_t checksum; icmp_hdr->icmp_type = icmp_type; icmp_hdr->icmp_code = icmp_code; icmp_hdr->icmp_sum = 0; checksum = cksum(icmp_hdr, icmp_len); icmp_hdr->icmp_sum = checksum; /* update IP info */ ip_hdr->ip_tos = 0; ip_hdr->ip_ttl = 64; uint32_t iptemp; iptemp = ip_hdr->ip_src; ip_hdr->ip_src = ip_hdr->ip_dst; ip_hdr->ip_dst = iptemp; /* update ip checksum. */ ip_hdr->ip_sum = 0; checksum = cksum(ip_hdr, sizeof(*ip_hdr)); ip_hdr->ip_sum = checksum; checksum = cksum(ip_hdr, sizeof(*ip_hdr)); if (checksum != 0xffff){ return -1; } struct sr_rt* best_rt = find_best_rt(sr->routing_table, ntohl(ip_hdr->ip_dst)); if (!best_rt) { return -1; } res = sr_send_packet(sr, icmp_message, size, best_rt->interface); if (res != 0) { return -1; } return 0; }
/* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(struct mbuf *m) { struct ip *ip; int hlen; DEBUG_CALL("ip_input"); DEBUG_ARG("m = %lx", (long)m); DEBUG_ARG("m_len = %d", m->m_len); ipstat.ips_total++; if (m->m_len < sizeof (struct ip)) { ipstat.ips_toosmall++; return; } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { ipstat.ips_badvers++; goto bad; } hlen = ip->ip_hl << 2; if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */ ipstat.ips_badhlen++; /* or packet too short */ goto bad; } /* keep ip header intact for ICMP reply * ip->ip_sum = cksum(m, hlen); * if (ip->ip_sum) { */ if(cksum(m,hlen)) { ipstat.ips_badsum++; goto bad; } /* * Convert fields to host representation. */ NTOHS(ip->ip_len); if (ip->ip_len < hlen) { ipstat.ips_badlen++; goto bad; } NTOHS(ip->ip_id); NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_len < ip->ip_len) { ipstat.ips_tooshort++; goto bad; } /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ if(ip->ip_ttl==0 || ip->ip_ttl==1) { icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); goto bad; } /* * Process options and, if not destined for us, * ship it on. ip_dooptions returns 1 when an * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ /* We do no IP options */ /* if (hlen > sizeof (struct ip) && ip_dooptions(m)) * goto next; */ /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. * (We could look in the reassembly queue to see * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) * * XXX This should fail, don't fragment yet */ if (ip->ip_off &~ IP_DF) { register struct ipq *fp; /* * Look for queue of fragments * of this datagram. */ for (fp = (struct ipq *) ipq.next; fp != &ipq; fp = (struct ipq *) fp->next) if (ip->ip_id == fp->ipq_id && ip->ip_src.s_addr == fp->ipq_src.s_addr && ip->ip_dst.s_addr == fp->ipq_dst.s_addr && ip->ip_p == fp->ipq_p) goto found; fp = 0; found: /* * Adjust ip_len to not reflect header, * set ip_mff if more fragments are expected, * convert offset of this to bytes. */ ip->ip_len -= hlen; if (ip->ip_off & IP_MF) ((struct ipasfrag *)ip)->ipf_mff |= 1; else ((struct ipasfrag *)ip)->ipf_mff &= ~1; ip->ip_off <<= 3; /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) { ipstat.ips_fragments++; ip = ip_reass((struct ipasfrag *)ip, fp); if (ip == 0) return; ipstat.ips_reassembled++; m = dtom(ip); } else if (fp) ip_freef(fp); } else ip->ip_len -= hlen; /* * Switch out to protocol's input routine. */ ipstat.ips_delivered++; switch (ip->ip_p) { case IPPROTO_TCP: tcp_input(m, hlen, (struct socket *)NULL); break; case IPPROTO_UDP: udp_input(m, hlen); break; case IPPROTO_ICMP: icmp_input(m, hlen); break; default: ipstat.ips_noproto++; m_free(m); } return; bad: m_freem(m); return; }
/* * Process a received ICMP message. */ void icmp_input(struct mbuf *m, int hlen) { register struct icmp *icp; register struct ip *ip=mtod(m, struct ip *); int icmplen=ip->ip_len; Slirp *slirp = m->slirp; DEBUG_CALL("icmp_input"); DEBUG_ARG("m = %p", m); DEBUG_ARG("m_len = %d", m->m_len); /* * Locate icmp structure in mbuf, and check * that its not corrupted and of at least minimum length. */ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ freeit: m_free(m); goto end_error; } m->m_len -= hlen; m->m_data += hlen; icp = mtod(m, struct icmp *); if (cksum(m, icmplen)) { goto freeit; } m->m_len += hlen; m->m_data -= hlen; DEBUG_ARG("icmp_type = %d", icp->icmp_type); switch (icp->icmp_type) { case ICMP_ECHO: ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { icmp_reflect(m); } else if (slirp->restricted) { goto freeit; } else { struct socket *so; struct sockaddr_storage addr; if ((so = socreate(slirp)) == NULL) goto freeit; if (icmp_send(so, m, hlen) == 0) { return; } if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); m_free(m); goto end_error; } so->so_m = m; so->so_ffamily = AF_INET; so->so_faddr = ip->ip_dst; so->so_fport = htons(7); so->so_lfamily = AF_INET; so->so_laddr = ip->ip_src; so->so_lport = htons(9); so->so_iptos = ip->ip_tos; so->so_type = IPPROTO_ICMP; so->so_state = SS_ISFCONNECTED; /* Send the packet */ addr = so->fhost.ss; sotranslate_out(so, &addr); if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) { DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", errno,strerror(errno))); icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); udp_detach(so); } } /* if ip->ip_dst.s_addr == alias_addr.s_addr */ break; case ICMP_UNREACH: /* XXX? report error? close socket? */ case ICMP_TIMXCEED: case ICMP_PARAMPROB: case ICMP_SOURCEQUENCH: case ICMP_TSTAMP: case ICMP_MASKREQ: case ICMP_REDIRECT: m_free(m); break; default: m_free(m); } /* swith */ end_error: /* m is m_free()'d xor put in a socket xor or given to ip_send */ return; }
/* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */ int ip_output(struct socket *so, struct mbuf *m0) { Slirp *slirp = m0->slirp; struct ip *ip; struct mbuf *m = m0; int hlen = sizeof(struct ip ); int len, off, error = 0; DEBUG_CALL("ip_output"); DEBUG_ARG("so = %lx", (long)so); DEBUG_ARG("m0 = %lx", (long)m0); ip = mtod(m, struct ip *); /* * Fill in IP header. */ ip->ip_v = IPVERSION; ip->ip_off &= IP_DF; ip->ip_id = htons(slirp->ip_id++); ip->ip_hl = hlen >> 2; /* * If small enough for interface, can just send directly. */ if ((u_int16_t)ip->ip_len <= IF_MTU) { ip->ip_len = htons((u_int16_t)ip->ip_len); ip->ip_off = htons((u_int16_t)ip->ip_off); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); if_output(so, m); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ if (ip->ip_off & IP_DF) { error = -1; goto bad; } len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */ if (len < 8) { error = -1; goto bad; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_nextpkt; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { struct ip *mhip; m = m_get(slirp); if (m == NULL) { error = -1; goto sendorfree; } m->m_data += IF_MAXLINKHDR; mhip = mtod(m, struct ip *); *mhip = *ip; m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); if (ip->ip_off & IP_MF) mhip->ip_off |= IP_MF; if (off + len >= (u_int16_t)ip->ip_len) len = (u_int16_t)ip->ip_len - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_int16_t)(len + mhlen)); if (m_copy(m, m0, off, len) < 0) { error = -1; goto sendorfree; } mhip->ip_off = htons((u_int16_t)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = cksum(m, mhlen); *mnext = m; mnext = &m->m_nextpkt; } /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m = m0; m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len); ip->ip_len = htons((u_int16_t)m->m_len); ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); sendorfree: for (m = m0; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = NULL; if (error == 0) if_output(so, m); else m_freem(m); } } done: return (error); bad: m_freem(m0); goto done; }
/*--------------------------------------------------------------------- * Method: sr_send_icmp(struct sr_instance* sr, uint8_t *packet, unsigned int len, * uint8_t type, uint8_t code) * Scope: Global * * This function sends an icmp of the supplied type and code, using the * supplied packet, which is an ip datagram. * *---------------------------------------------------------------------*/ void sr_send_icmp(struct sr_instance* sr, uint8_t *packet, unsigned int len, uint8_t type, uint8_t code) { uint16_t icmp_len; uint32_t dst; struct sr_ip_hdr *error_ip_hdr; struct sr_icmp_hdr icmp_hdr; struct sr_icmp_hdr *icmp_hdr_ptr; struct sr_if *interface; struct sr_ip_hdr ip_hdr; uint8_t *new_pkt; struct sr_rt *rt; uint16_t total_len; /* Destination unreachable message or TTL exceeded. */ if (type == ICMP_UNREACHABLE_TYPE || type == ICMP_TIME_EXCEEDED_TYPE) { /* Update icmp header fields. */ icmp_hdr.icmp_type = type; icmp_hdr.icmp_code = code; icmp_hdr.unused = 0; icmp_hdr.icmp_sum = 0; /* Update the IP header fields. */ error_ip_hdr = (struct sr_ip_hdr *)packet; ip_hdr.ip_hl = ICMP_IP_HDR_LEN; ip_hdr.ip_v = ip_version_4; ip_hdr.ip_tos = 0; ip_hdr.ip_id = error_ip_hdr->ip_id; ip_hdr.ip_off = htons(IP_DF); ip_hdr.ip_ttl = DEFAULT_TTL; ip_hdr.ip_p = ip_protocol_icmp; ip_hdr.ip_sum = 0; ip_hdr.ip_dst = error_ip_hdr->ip_src; dst = error_ip_hdr->ip_src; /* Look up longest prefix match in your routing table. If it doesn't exist, just * give up. No use in sending an error message for an error message. */ rt = sr_longest_prefix_match(sr, ip_in_addr(ip_hdr.ip_dst)); if (rt == 0) return; /* Update the source IP to be the outgoing interface's ip address. */ interface = sr_get_interface(sr, (const char*)rt->interface); ip_hdr.ip_src = interface->ip; /* Update length: first 8 bytes of original message, original ip header, icmp header * and new ip header. */ icmp_len = ip_ihl(error_ip_hdr) + ICMP_COPIED_DATAGRAM_DATA_LEN + sizeof(struct sr_icmp_hdr); total_len = icmp_len + ICMP_IP_HDR_LEN_BYTES; ip_hdr.ip_len = htons(total_len); /* Update the ip checksum. */ ip_hdr.ip_sum = cksum(&ip_hdr, ICMP_IP_HDR_LEN_BYTES); /* Allocate a packet, copy everything in. */ new_pkt = malloc(total_len); memcpy(new_pkt, &ip_hdr, ICMP_IP_HDR_LEN_BYTES); memcpy(new_pkt + ICMP_IP_HDR_LEN_BYTES, &icmp_hdr, sizeof(struct sr_icmp_hdr)); memcpy(new_pkt + ICMP_IP_HDR_LEN_BYTES + sizeof(struct sr_icmp_hdr), error_ip_hdr, ip_ihl(error_ip_hdr) + ICMP_COPIED_DATAGRAM_DATA_LEN); /* Echo reply. */ } else if (type == ICMP_ECHO_REPLY_CODE) { /* Update the IP header fields. */ error_ip_hdr = (struct sr_ip_hdr *)packet; dst = error_ip_hdr->ip_src; error_ip_hdr->ip_src = error_ip_hdr->ip_dst; error_ip_hdr->ip_dst = dst; /* Update the type of icmp from request to reply. */ icmp_hdr_ptr = icmp_header(error_ip_hdr); icmp_hdr_ptr->icmp_sum = 0; icmp_hdr_ptr->icmp_code = code; icmp_hdr_ptr->icmp_type = type; /* Allocate a copy of this packet. */ total_len = ip_len(error_ip_hdr); new_pkt = malloc(total_len); memcpy(new_pkt, error_ip_hdr, total_len); /* Calculate the length of the icmp message for computing checksum. */ icmp_len = total_len - ICMP_IP_HDR_LEN; } /* Update the checksum of the icmp message starting at 'type' field. */ icmp_hdr_ptr = icmp_header((struct sr_ip_hdr *)new_pkt); icmp_hdr_ptr->icmp_sum = cksum(icmp_hdr_ptr, icmp_len); /* Encapsulate and send */ sr_encap_and_send_pkt(sr, new_pkt, total_len, dst, 0, ethertype_ip); free(new_pkt); }
void sr_handlepacket_ip(struct sr_instance* sr, sr_ethernet_hdr_t* eth_hdr, uint8_t* packet/* not even a malloc pointer, don't free this */, unsigned int len, char* interface/* lent */) { /* puts("handling IP header"); */ sr_ip_hdr_t *ip_hdr; struct sr_if* if_dst; /* REQUIRES */ assert(packet); if (len < sizeof(sr_ip_hdr_t)) { /* puts("Ethernet payload (claiming to contain IP) is smaller than IP header. Discarding."); */ return; } /* If we're using a NAT, then the rewrite of IP packets is * absolutely the first thing we do. That way the rest of * the router still feels like everything is the same. This * is the only change to the router to get a NAT working. */ if (sr->use_nat) { int drop_requested = sr_nat_rewrite_ip_packet(sr, packet, len); if (drop_requested) { /* puts("DROP requested by the NAT. Dropping packet."); */ return; } } ip_hdr = (sr_ip_hdr_t*)packet; /* Endianness */ uint32_t ip_dst = ntohl(ip_hdr->ip_dst); /* Check for a corrupt packet */ uint16_t cksum_buffer = ip_hdr->ip_sum; ip_hdr->ip_sum = 0; if (cksum((const void*)ip_hdr, sizeof(sr_ip_hdr_t)) != cksum_buffer) { /* puts("Checksum is corrupted. Bailing"); */ return; } /* Decrement time to live */ /* printf("TTL: %i\n", ip_hdr->ip_ttl); */ /* Recalculate the cksum after changing the TTL */ ip_hdr->ip_ttl--; ip_hdr->ip_sum = cksum((const void*)ip_hdr, sizeof(sr_ip_hdr_t)); if (ip_hdr->ip_ttl <= 0) { /* puts("Packet TTL expired"); */ /* Send out a ICMP TTL expired response */ sr_try_send_ip_packet(sr, ip_hdr->ip_src, 0, sr_build_icmp_t3_packet( ICMP_TYPE_TTL_EXCEEDED, ICMP_CODE_TTL_EXCEEDED, (uint8_t*)ip_hdr ), NULL ); return; } /* Check for any IP packets destined for our interfaces */ if_dst = sr_get_interface_ip (sr, ntohl(ip_dst)); if (if_dst != 0) { /* puts("IP packet is destined for our interfaces."); */ /* If this IP packet is destined for us */ switch (ip_hdr->ip_p) { /* * ICMP handling. */ case ip_protocol_icmp: sr_handlepacket_icmp(sr, eth_hdr, ip_hdr, packet + sizeof(sr_ip_hdr_t), len - sizeof(sr_ip_hdr_t), interface); return; /* * TCP and UDP handling, which are both the same */ case ip_protocol_tcp: case ip_protocol_udp: /* puts("Received a TCP/UDP request."); */ /* Undo changes to the TTL */ ip_hdr->ip_ttl++; ip_hdr->ip_sum = cksum((const void*)ip_hdr, sizeof(sr_ip_hdr_t)); /* Send out a ICMP port unreachable response */ sr_try_send_ip_packet(sr, ip_hdr->ip_src, ip_hdr->ip_dst, sr_build_icmp_t3_packet( ICMP_TYPE_PORT_UNREACHABLE, ICMP_CODE_PORT_UNREACHABLE, (uint8_t*)ip_hdr ), NULL ); return; } /* puts("IP packet protocol unsupported. Dropping packet."); */ return; } /* Build a packet struct to pass in the contents of the IP we're forwarding */ uint8_t* payload_buf = ((uint8_t*)packet) + sizeof(sr_ip_hdr_t); int payload_len = len - sizeof(sr_ip_hdr_t); sr_constructed_packet_t *payload = sr_grow_or_create_payload(NULL, payload_len); memcpy(payload->buf, payload_buf, payload_len); /* Handles checking the routing table, and making any ARP requests we need to make */ sr_try_send_ip_packet(sr, ip_hdr->ip_dst, 0, payload, ip_hdr); }
void ip_forwardpacket(struct sr_instance *sr, sr_ip_hdr_t *ip_hdr, unsigned int len, char *interface) { printf("** FORWARDING\n"); struct sr_arpreq *req; struct sr_arpentry *arp_entry; uint8_t icmp_type; uint8_t icmp_code; ip_hdr->ip_ttl --; /* Update checksum */ ip_hdr->ip_sum = 0; ip_hdr->ip_sum = cksum(ip_hdr, ip_hdr->ip_hl * 4); uint8_t *ip_pkt; ip_pkt = malloc(len); memcpy(ip_pkt, ip_hdr, len); /* Find longest prefix match in routing table */ struct sr_rt* lpmatch = longest_prefix_matching(sr, ip_hdr->ip_dst); /* If cannot find destination IP in routing table, send ICMP net unreachable */ /* OR TTL = 0 */ if (lpmatch == 0) { icmp_type = 3; icmp_code = 0; sr_icmp_with_payload(sr, ip_hdr, interface, icmp_type, icmp_code); return; } else if (ip_hdr->ip_ttl == 0){ icmp_type = 11; icmp_code = 0; sr_icmp_with_payload(sr, (sr_ip_hdr_t *)(ip_pkt), interface, icmp_type, icmp_code); return; } /* Ready to forward packet */ /* Get the corresponding interface of the destination IP. */ struct sr_if* s_interface = sr_get_interface(sr, lpmatch->interface); /* Check ARP cache */ arp_entry = sr_arpcache_lookup(&sr->cache, lpmatch->gw.s_addr); if (arp_entry == 0){ /* If miss APR cache, add the packet to ARP request queue */ req = sr_arpcache_queuereq(&sr->cache, lpmatch->gw.s_addr, ip_pkt, len, s_interface->name); sr_handle_arpreq(sr, req); } else { /* Hit ARP cache, send out the packet right away using next-hop */ /* Encap the ARP request into ethernet frame and then send it */ sr_ethernet_hdr_t sr_ether_pkt; memcpy(sr_ether_pkt.ether_dhost, arp_entry->mac, ETHER_ADDR_LEN); /* Address from routing table */ memcpy(sr_ether_pkt.ether_shost, s_interface->addr, ETHER_ADDR_LEN); /* Hardware address of the outgoing interface */ sr_ether_pkt.ether_type = htons(ethertype_ip); uint8_t *packet_rqt; unsigned int total_len = len + sizeof(struct sr_ethernet_hdr); packet_rqt = malloc(total_len); memcpy(packet_rqt, &(sr_ether_pkt), sizeof(sr_ether_pkt)); memcpy(packet_rqt + sizeof(sr_ether_pkt), ip_pkt, len); /* Forward the IP packet*/ sr_send_packet(sr, packet_rqt, total_len, s_interface->name); free(packet_rqt); } }
/******************************************************************* * Every time handle_packet() is called, update_buffer() is called. The function walks through * the packet buffer and checks if the necessary mac address is now in the arp cache. If it is, * then the MAC address is added to the ethernet header and the packet is send and removed * from the buffer. If the address is not in the cache and less than 5 arp requests have already * been sent, then another arp request is sent. Otherwise the packet is deleted from the buffer * and an ICMP port unreachable is sent. *******************************************************************/ void update_buffer(struct packet_state* ps,struct packet_buffer* queue) { struct packet_buffer* buf_walker=0; buf_walker=queue; while(buf_walker) { uint32_t search_ip=buf_walker->gw_IP; struct arp_cache_entry* ent=search_cache(ps, search_ip); if(ent!=NULL) /*MAC Address is in ARP Cache. Send packet. */ { struct sr_ethernet_hdr *eth = (struct sr_ethernet_hdr *)(buf_walker->packet); memmove(eth->ether_dhost, ent->mac, ETHER_ADDR_LEN); struct sr_if *iface=sr_get_interface(ps->sr, buf_walker->interface); memmove(eth->ether_shost, iface->addr, ETHER_ADDR_LEN); eth->ether_type = htons(ETHERTYPE_IP); sr_send_packet(ps->sr, buf_walker->packet, buf_walker->pack_len, buf_walker->interface); buf_walker=delete_from_buffer(ps,buf_walker); } else if(buf_walker->num_arp_reqs < 5) /*Send another arp request. */ { buf_walker->num_arp_reqs++; sr_send_packet(ps->sr, buf_walker->arp_req, buf_walker->arp_len, buf_walker->interface); buf_walker=buf_walker->next; } else /* 5 ARP Request already sent, send ICMP Port Unreachable and Delete from Buffer.*/ { int off = sizeof(struct sr_ethernet_hdr) + sizeof(struct ip); ps->res_len=off; ps->response += sizeof(struct sr_ethernet_hdr); struct ip *res_ip = (struct ip*) ps->response; /*IP Header for ICMP Port Unreachable*/ ps->response += sizeof(struct ip); ps->packet = buf_walker->packet; ps->packet += sizeof(struct sr_ethernet_hdr); struct ip *ip_hdr = (struct ip*) (ps->packet); /*IP Header from original packet. */ ps->packet += sizeof(struct ip); icmp_response(ps, ip_hdr, ICMPT_DESTUN, ICMPC_HOSTUN); /*Construct ICMP */ memmove(res_ip, ip_hdr, sizeof(struct ip)); res_ip->ip_len = htons(ps->res_len - sizeof(struct sr_ethernet_hdr)); res_ip->ip_ttl = INIT_TTL; res_ip->ip_tos = ip_hdr->ip_tos; res_ip->ip_p = IPPROTO_ICMP; /* Finding interface to send ICMP out of*/ struct sr_rt* iface_rt_entry=get_routing_if(ps, ip_hdr->ip_src); struct sr_if* iface=sr_get_interface(ps->sr, iface_rt_entry->interface); res_ip->ip_src.s_addr = iface->ip; res_ip->ip_dst = ip_hdr->ip_src; res_ip->ip_sum = 0; res_ip->ip_sum = cksum((uint8_t *)res_ip, sizeof(struct ip)); res_ip->ip_sum = htons(res_ip->ip_sum); ps->response = (uint8_t *) res_ip - sizeof(struct sr_ethernet_hdr); struct sr_ethernet_hdr* eth_resp=(struct sr_ethernet_hdr*)ps->response; memmove(eth_resp->ether_dhost,buf_walker->old_eth->ether_shost,ETHER_ADDR_LEN); memmove(eth_resp->ether_shost,iface->addr, ETHER_ADDR_LEN); eth_resp->ether_type=htons(ETHERTYPE_IP); sr_send_packet(ps->sr, ps->response, ps->res_len, iface_rt_entry->interface); buf_walker=delete_from_buffer(ps,buf_walker); } } }
void sr_icmp_with_payload(struct sr_instance *sr, sr_ip_hdr_t *ip_hdr, char *interface, uint8_t icmp_type, uint8_t icmp_code) { struct sr_if *r_interface = sr_get_interface(sr, interface); struct sr_arpreq *req; struct sr_arpentry *arp_entry; uint8_t *cache_packet; uint16_t total_len; uint16_t icmp_len; uint32_t dst; if (icmp_type == 11 && icmp_code == 0){ if (natEnabled(sr)){ sr_nat_undo_mapping(sr, ip_hdr, ip_hdr->ip_len, r_interface); } } /* Create a new IP packet for ICMP message */ struct sr_ip_hdr send_ip_hdr; send_ip_hdr.ip_hl = 5; send_ip_hdr.ip_v = ip_hdr->ip_v; send_ip_hdr.ip_tos = 0; send_ip_hdr.ip_id = 0; send_ip_hdr.ip_off = htons(IP_DF); send_ip_hdr.ip_ttl = DEFAULT_TTL; send_ip_hdr.ip_p = ip_protocol_icmp; send_ip_hdr.ip_sum = 0; send_ip_hdr.ip_dst = ip_hdr->ip_src; if (icmp_type == 3 && icmp_code == 3){ ip_hdr->ip_sum = cksum(ip_hdr, ip_hdr->ip_hl * 4); send_ip_hdr.ip_src = ip_hdr->ip_dst; } else { send_ip_hdr.ip_src = r_interface->ip; } dst = ip_hdr->ip_src; struct sr_icmp_t3_hdr error_packet; error_packet.icmp_type = icmp_type; error_packet.icmp_code = icmp_code; error_packet.icmp_sum = 0; error_packet.unused = 0; error_packet.next_mtu = htons(MTU); icmp_len = sizeof(struct sr_icmp_t3_hdr); total_len = ICMP_IP_HDR_LEN_BYTE + icmp_len; send_ip_hdr.ip_len = htons(total_len); send_ip_hdr.ip_sum = cksum(&send_ip_hdr, ICMP_IP_HDR_LEN_BYTE); cache_packet = malloc(total_len); memcpy(error_packet.data, ip_hdr, ICMP_DATA_SIZE); memcpy(cache_packet, &(send_ip_hdr), ICMP_IP_HDR_LEN_BYTE); memcpy(cache_packet + ICMP_IP_HDR_LEN_BYTE, &(error_packet), sizeof(struct sr_icmp_t3_hdr)); struct sr_icmp_hdr *icmp_hdr_ptr = icmp_header((struct sr_ip_hdr *)cache_packet); icmp_hdr_ptr->icmp_sum = cksum(icmp_hdr_ptr, icmp_len); /*Check if we should send immediately or wait */ arp_entry = sr_arpcache_lookup(&sr->cache, dst); if (arp_entry != 0){ /* Entry exists, we can send it out right now */ sr_add_ethernet_send(sr, cache_packet, total_len, dst, ethertype_ip); } else { req = sr_arpcache_queuereq(&sr->cache, dst, cache_packet, total_len, interface); sr_handle_arpreq(sr, req); } }
/* This function will free m0! */ int ip_output0(PNATState pData, struct socket *so, struct mbuf *m0, int urg) { register struct ip *ip; register struct mbuf *m = m0; register int hlen = sizeof(struct ip); int len, off, error = 0; struct ethhdr *eh = NULL; uint8_t eth_dst[ETH_ALEN]; int rc = 1; STAM_PROFILE_START(&pData->StatIP_output, a); #ifdef LOG_ENABLED LogFlowFunc(("ip_output: so = %R[natsock], m0 = %lx\n", so, (long)m0)); #else NOREF(so); #endif M_ASSERTPKTHDR(m); Assert(m->m_pkthdr.header); #if 0 /* We do no options */ if (opt) { m = ip_insertoptions(m, opt, &len); hlen = len; } #endif ip = mtod(m, struct ip *); LogFunc(("ip(src:%RTnaipv4, dst:%RTnaipv4)\n", ip->ip_src, ip->ip_dst)); /* * Fill in IP header. */ ip->ip_v = IPVERSION; ip->ip_off &= IP_DF; ip->ip_id = RT_H2N_U16(ip_currid++); ip->ip_hl = hlen >> 2; ipstat.ips_localout++; /* Current TCP/IP stack hasn't routing information at * all so we need to calculate destination ethernet address */ rc = rt_lookup_in_cache(pData, ip->ip_dst.s_addr, eth_dst); if (RT_FAILURE(rc)) goto exit_drop_package; eh = (struct ethhdr *)(m->m_data - ETH_HLEN); /* * If small enough for interface, can just send directly. */ if ((u_int16_t)ip->ip_len <= if_mtu) { ip->ip_len = RT_H2N_U16((u_int16_t)ip->ip_len); ip->ip_off = RT_H2N_U16((u_int16_t)ip->ip_off); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); if (!(m->m_flags & M_SKIP_FIREWALL)){ struct m_tag *t; STAM_PROFILE_START(&pData->StatALIAS_output, b); if ((t = m_tag_find(m, PACKET_TAG_ALIAS, NULL)) != 0) rc = LibAliasOut((struct libalias *)&t[1], mtod(m, char *), m_length(m, NULL)); else rc = LibAliasOut(pData->proxy_alias, mtod(m, char *), m_length(m, NULL)); if (rc == PKT_ALIAS_IGNORED) { Log(("NAT: packet was droppped\n")); goto exit_drop_package; } STAM_PROFILE_STOP(&pData->StatALIAS_output, b); }
/* Packet size. */ *size = sizeof(struct iphdr) + greoptlen + sizeof(struct igmphdr); /* Try to reallocate packet, if necessary */ alloc_packet(*size); /* IP Header structure making a pointer to Packet. */ ip = ip_header(packet, *size, co); /* GRE Encapsulation takes place. */ gre_encapsulation(packet, co, sizeof(struct iphdr) + sizeof(struct igmphdr)); /* IGMPv1 Header structure making a pointer to Packet. */ igmpv1 = (struct igmphdr *)((void *)(ip + 1) + greoptlen); igmpv1->type = co->igmp.type; igmpv1->code = co->igmp.code; igmpv1->group = htonl(INADDR_RND(co->igmp.group)); igmpv1->csum = 0; /* Needed 'cause cksum() call, below! */ /* Computing the checksum. */ igmpv1->csum = co->bogus_csum ? RANDOM() : cksum(igmpv1, sizeof(struct igmphdr)); /* GRE Encapsulation takes place. */ gre_checksum(packet, co, *size); }
/* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(struct mbuf *m) { Slirp *slirp = m->slirp; register struct ip *ip; int hlen; if (!slirp->in_enabled) { goto bad; } DEBUG_CALL("ip_input"); DEBUG_ARG("m = %p", m); DEBUG_ARG("m_len = %d", m->m_len); if (m->m_len < sizeof (struct ip)) { goto bad; } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { goto bad; } hlen = ip->ip_hl << 2; if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */ goto bad; /* or packet too short */ } /* keep ip header intact for ICMP reply * ip->ip_sum = cksum(m, hlen); * if (ip->ip_sum) { */ if(cksum(m,hlen)) { goto bad; } /* * Convert fields to host representation. */ NTOHS(ip->ip_len); if (ip->ip_len < hlen) { goto bad; } NTOHS(ip->ip_id); NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_len < ip->ip_len) { goto bad; } /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ if (ip->ip_ttl == 0) { icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl"); goto bad; } /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. * (We could look in the reassembly queue to see * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) * * XXX This should fail, don't fragment yet */ if (ip->ip_off &~ IP_DF) { register struct ipq *fp; struct qlink *l; /* * Look for queue of fragments * of this datagram. */ for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link; l = l->next) { fp = container_of(l, struct ipq, ip_link); if (ip->ip_id == fp->ipq_id && ip->ip_src.s_addr == fp->ipq_src.s_addr && ip->ip_dst.s_addr == fp->ipq_dst.s_addr && ip->ip_p == fp->ipq_p) goto found; } fp = NULL; found: /* * Adjust ip_len to not reflect header, * set ip_mff if more fragments are expected, * convert offset of this to bytes. */ ip->ip_len -= hlen; if (ip->ip_off & IP_MF) ip->ip_tos |= 1; else ip->ip_tos &= ~1; ip->ip_off <<= 3; /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ if (ip->ip_tos & 1 || ip->ip_off) { ip = ip_reass(slirp, ip, fp); if (ip == NULL) return; m = dtom(slirp, ip); } else if (fp) ip_freef(slirp, fp); } else
int main(int argc, char** argv) { const char* hostname = argc > 1 ? argv[1] : "google.com"; fprintf(stdout, "Sending ICMP request to '%s'\n", hostname); struct hostent* h = gethostbyname(hostname); if(0 == h || h->h_addr_list[0] == 0) { fprintf(stderr, "Unable to resolve host '%s'\n", hostname); return EXIT_FAILURE; } int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { fprintf(stderr, "Unable to create socket, reason: %s\n", strerror(errno)); return EXIT_FAILURE; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); /* I want to get an IPv4 IP address */ ifr.ifr_addr.sa_family = AF_INET; /* I want IP address attached to "eth0" */ strncpy(ifr.ifr_name, "en3", IFNAMSIZ-1); ioctl(s, SIOCGIFADDR, &ifr); /* display result */ fprintf(stdout, "%s\n", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); const int on = 1; // We shall provide IP headers if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&on, sizeof (on)) == -1) { fprintf(stderr, "Unable to setsockopt IPPROTO_IP IP_HDRINCL, reason: %s\n", strerror(errno)); return EXIT_FAILURE; } // Allow socket to send datagrams to broadcast addresses if (setsockopt (s, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof (on)) == -1) { fprintf(stderr, "Unable to setsockopt SOL_SOCKET SO_BROADCAST, reason: %s\n", strerror(errno)); return EXIT_FAILURE; } int payload_size = 128; //Calculate total packet size int packet_size = sizeof (struct ip) + sizeof (struct icmp) + payload_size; char *packet = (char *) malloc (packet_size); //ip header struct ip *ip = (struct ip *) packet; struct icmp *icmp = (struct icmp *) (packet + sizeof (struct ip)); //zero out the packet buffer memset (packet, 0, packet_size); // See - http://www.kernelthread.com/projects/hanoi/src/hanoimania/hanoi-icmp.c ip->ip_v = 4; ip->ip_hl = 5; ip->ip_tos = 0; ip->ip_len = sizeof(struct ip) + sizeof(struct icmp); // htons (packet_size); ip->ip_id = rand (); ip->ip_off = 0; ip->ip_ttl = 255; ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = inet_addr("10.0.0.17"); // *(in_addr_t*)&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; ip->ip_dst.s_addr = *(in_addr_t*)h->h_addr_list[0]; //ip->check = in_cksum ((u16 *) ip, sizeof (struct iphdr)); icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_hun.ih_idseq.icd_seq = rand(); icmp->icmp_hun.ih_idseq.icd_id = rand(); //checksum icmp->icmp_cksum = 0; struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = *(in_addr_t*)h->h_addr_list[0]; // *(in_addr_t*)h->h_addr_list[0]; memset(&servaddr.sin_zero, 0, sizeof (servaddr.sin_zero)); struct sockaddr_in connection; socklen_t addrlen; char buffer[MAX_MTU]; int sent = 0, sent_size = 0; while (1) { // icmp->icmp_hun.ih_idseq.icd_seq = rand(); // icmp->icmp_hun.ih_idseq.icd_id = rand(); memset(packet + sizeof(struct ip) + sizeof(struct icmp), rand() % 255, payload_size); //recalculate the icmp header checksum since we are filling the payload with random characters everytime icmp->icmp_cksum = 0; icmp->icmp_cksum = cksum((unsigned short *)icmp, sizeof(struct icmp)); ip->ip_sum = cksum((unsigned short*)ip, sizeof(struct ip)); if (sendto(s, packet, packet_size, 0, (struct sockaddr*) &servaddr, sizeof (servaddr)) != packet_size) { fprintf(stderr, "Unable to send packet, reason: %s\n", strerror(errno)); break; } ++sent; printf("%d packets sent\r", sent); fflush(stdout); usleep(1e6); //microseconds } free(packet); close(s); return EXIT_SUCCESS; }
uint8_t* sr_generate_icmp(sr_ethernet_hdr_t *received_ether_hdr, sr_ip_hdr_t *received_ip_hdr, struct sr_if *iface, uint8_t type, uint8_t code) { uint8_t *reply_packet = 0; sr_icmp_hdr_t *icmp_hdr = 0; sr_ip_hdr_t *ip_hdr = 0; sr_ethernet_hdr_t *ether_hdr = 0; size_t icmp_size = 0; /* type 0 echo reply */ if (type == 0) { /* create new reply packet */ if ((reply_packet = malloc(sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_hdr_t))) == NULL) { fprintf(stderr,"Error: out of memory (sr_generate_icmp)\n"); return 0; } /* construct ICMP header */ icmp_hdr = (sr_icmp_hdr_t *)(reply_packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); icmp_hdr->icmp_type = htons(type); icmp_hdr->icmp_code = htons(code); icmp_hdr->icmp_sum = htons(0); icmp_hdr->icmp_sum = cksum(icmp_hdr, sizeof(sr_icmp_hdr_t)); /* grab the size of ICMP header */ icmp_size = sizeof(sr_icmp_hdr_t); } /* Destination net unreachable (type 3, code 0) OR Time exceeded (type 11, code 0), since the two types use the exact same struct, except the next_mtu field which is unused for type 11 */ else if (type == 3 || type == 11) { sr_icmp_t3_hdr_t* icmp_hdr; /* create new reply packet */ if ((reply_packet = malloc(sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_t3_hdr_t))) == NULL) { fprintf(stderr,"Error: out of memory (sr_generate_icmp)\n"); return 0; } /* construct ICMP header */ icmp_hdr = (sr_icmp_t3_hdr_t *)(reply_packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t)); icmp_hdr->icmp_type = htons(type); icmp_hdr->icmp_code = htons(code); icmp_hdr->unused = htons(0); icmp_hdr->next_mtu = htons(0); if (type == 3) { /* only set next_mtu if ICMP type is 3*/ icmp_hdr->next_mtu = htons(1500); } memcpy(icmp_hdr->data, received_ip_hdr, ICMP_DATA_SIZE); icmp_hdr->icmp_sum = htons(0); icmp_hdr->icmp_sum = cksum(icmp_hdr, sizeof(sr_icmp_t3_hdr_t)); /* grab the size of ICMP header */ icmp_size = sizeof(sr_icmp_t3_hdr_t); } /* An ICMP type that we can't handle */ else { fprintf(stderr,"Error: unsupported ICMP type (sr_generate_icmp)\n"); return 0; } /* construct IP header */ ip_hdr = (sr_ip_hdr_t *)(reply_packet + sizeof(sr_ethernet_hdr_t)); ip_hdr->ip_hl = htons(5); /* header length */ ip_hdr->ip_v = htons(4); /* version */ ip_hdr->ip_tos = htons(0); /* type of service */ ip_hdr->ip_len = htons(20 + icmp_size); /* total length */ ip_hdr->ip_id = htons(0); /* identification */ ip_hdr->ip_off = htons(IP_DF); /* fragment offset field */ ip_hdr->ip_ttl = htons(INIT_TTL); /* time to live */ ip_hdr->ip_p = htons(ip_protocol_icmp); /* protocol */ ip_hdr->ip_src = htonl(iface->ip); /* source ip address */ ip_hdr->ip_dst = received_ip_hdr->ip_src; /* dest ip address */ ip_hdr->ip_sum = htons(0); ip_hdr->ip_sum = cksum(ip_hdr, sizeof(sr_ip_hdr_t)); /* checksum */ /* construct ethernet header */ ether_hdr = (sr_ethernet_hdr_t*)reply_packet; memcpy(ether_hdr->ether_dhost, received_ether_hdr->ether_shost, ETHER_ADDR_LEN); memcpy(ether_hdr->ether_shost, iface->addr, sizeof(iface->addr)); ether_hdr->ether_type = htons(ethertype_ip); return reply_packet; }