/* * Copyright (c) 1997, 1998 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the MASH Research * Group at the University of California, Berkeley. * 4. Neither the name of the University nor of the Research Group may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/emulate/tcptap.cc,v 1.4 2002/09/23 23:25:05 alefiyah Exp $ (ISI)"; #endif #include "tcptap.h" static class TCPTapAgentClass : public TclClass { public: TCPTapAgentClass() : TclClass("Agent/TCPTap") {} TclObject* create(int, const char*const*) { return (new TCPTapAgent()); } } class_tcptap_agent; TCPTapAgent::TCPTapAgent() { adv_window = DEFAULT_ADV_WINDOW; bzero(&extnode, sizeof(struct sockaddr_in)); extnode.sin_port = DEFAULT_EXT_PORT; extnode.sin_addr.s_addr = inet_addr(DEFAULT_EXT_ADDR); extnode.sin_family = AF_INET; bzero(&nsnode, sizeof(struct sockaddr_in)); nsnode.sin_port = DEFAULT_NS_PORT; nsnode.sin_addr.s_addr = inet_addr(DEFAULT_NS_ADDR); nsnode.sin_family = AF_INET; dropp = 0; } /* * Methods to set ip addresses and port numbers from Tcl scripts. */ int TCPTapAgent::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "nsipaddr") == 0) { if ((nsnode.sin_addr.s_addr = inet_addr(argv[2])) == INADDR_NONE) { printf("Error setting ns ip address"); exit(1); } return (TCL_OK); } if (strcmp(argv[1], "extipaddr") == 0) { if ((extnode.sin_addr.s_addr = inet_addr(argv[2])) == INADDR_NONE) { printf("Error setting external ip address"); exit(1); } return (TCL_OK); } if (strcmp(argv[1], "extport") == 0) { extnode.sin_port = atoi(argv[2]); return (TCL_OK); } if (strcmp(argv[1], "advertised-window") == 0) { adv_window = atoi(argv[2]); return (TCL_OK); } } /* if (argc == 3) */ return (TapAgent::command(argc, argv)); } unsigned short TCPTapAgent::trans_check(unsigned char proto, char *packet, int length, struct in_addr source_address, struct in_addr dest_address) { char *pseudo_packet; unsigned short answer; pseudohdr.protocol = proto; pseudohdr.length = htons(length); pseudohdr.place_holder = 0; pseudohdr.source_address = source_address; pseudohdr.dest_address = dest_address; if((pseudo_packet = (char *) malloc(sizeof(pseudohdr) + length)) == NULL) { perror("malloc"); exit(1); } memcpy(pseudo_packet,&pseudohdr,sizeof(pseudohdr)); memcpy((pseudo_packet + sizeof(pseudohdr)), packet,length); answer = (unsigned short)in_cksum((unsigned short *)pseudo_packet, (length + sizeof(pseudohdr))); free(pseudo_packet); return answer; } /* * Taken from the raw-sock program */ unsigned short TCPTapAgent::in_cksum(unsigned short *addr, int len) { register int sum = 0; u_short answer = 0; register u_short *w = addr; register int nleft = len; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } void TCPTapAgent::ip_gen(char *packet, unsigned char protocol, struct in_addr saddr, struct in_addr daddr, unsigned short length, unsigned char ttl) { struct ip *ipheader; ipheader = (struct ip *) packet; bzero((void *) ipheader, IP_HEADER_LEN); ipheader->ip_hl = (IP_HEADER_LEN / 4); ipheader->ip_v = IPVERSION; ipheader->ip_len = length; ipheader->ip_id = 0; ipheader->ip_ttl = ttl; ipheader->ip_p = protocol; ipheader->ip_src = saddr; ipheader->ip_dst = daddr; ipheader->ip_len = ntohs(ipheader->ip_len); ipheader->ip_off = ntohs(ipheader->ip_off); ipheader->ip_sum = (unsigned short) in_cksum((unsigned short *) ipheader, sizeof(struct ip)); } void TCPTapAgent::tcp_gen(char *packet, unsigned short sport, unsigned short dport, Packet *nsp) { struct tcphdr *tcpheader; hdr_tcp* ns_tcphdr = HDR_TCP(nsp); tcpheader = (struct tcphdr *) packet; memset((char *)tcpheader, '\0', sizeof(struct tcphdr)); #ifndef LINUX_TCP_HEADER tcpheader->th_sport = htons(sport); tcpheader->th_dport = htons(dport); tcpheader->th_seq = htonl(ns_tcphdr->seqno_); tcpheader->th_ack = htonl(ns_tcphdr->ackno_); tcpheader->th_off = (TCP_HEADER_LEN / 4); tcpheader->th_win = htons(adv_window); /* Here we only propogate the "well-known" flags. If you want to add other flags, uncomment this line and comment the rest. tcpheader->th_flags = ns_tcphdr->tcp_flags_; */ tcpheader->th_flags = 0; if (ns_tcphdr->tcp_flags_ & TH_FIN) tcpheader->th_flags |= TH_FIN; if (ns_tcphdr->tcp_flags_ & TH_SYN) tcpheader->th_flags |= TH_SYN; if (ns_tcphdr->tcp_flags_ & TH_RST) tcpheader->th_flags |= TH_RST; if (ns_tcphdr->tcp_flags_ & TH_PUSH) tcpheader->th_flags |= TH_PUSH; if (ns_tcphdr->tcp_flags_ & TH_ACK) tcpheader->th_flags |= TH_ACK; if (ns_tcphdr->tcp_flags_ & TH_URG) tcpheader->th_flags |= TH_URG; #else /* LINUX_TCP_HEADER */ #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 tcpheader->source = htons(sport); tcpheader->dest = htons(dport); tcpheader->seq = htonl(ns_tcphdr->seqno_); tcpheader->ack_seq = htonl(ns_tcphdr->ackno_); tcpheader->doff = (TCP_HEADER_LEN / 4); tcpheader->window = htons(adv_window); /* Set the appropriate flag bits */ if (ns_tcphdr->tcp_flags_ & TH_FIN) tcpheader->fin= 1; if (ns_tcphdr->tcp_flags_ & TH_SYN) tcpheader->syn = 1; if (ns_tcphdr->tcp_flags_ & TH_RST) tcpheader->rst =1; if (ns_tcphdr->tcp_flags_ & TH_PUSH) tcpheader->psh = 1; if (ns_tcphdr->tcp_flags_ & TH_ACK) tcpheader->ack = 1; if (ns_tcphdr->tcp_flags_ & TH_URG) tcpheader->urg =1; #endif /* End of LINUX_TCP_HEADER */ } void TCPTapAgent::pkt_handler(void *clientdata, Packet *p, const struct timeval &ts) { TCPTapAgent *inst = (TCPTapAgent *)clientdata; inst->processpkt(p, ts); } void TCPTapAgent::processpkt(Packet *p, const struct timeval &ts) { struct ip *ipheader; struct tcphdr *tcpheader; unsigned char *buf; /* Ip header information from the grabbed packet. */ unsigned char ttl; /* Code to drop packet, if needed dropp++; if ((dropp % 10) == 0) { fprintf(stdout, "Dropping packet number : %d\n", dropp); Packet::free(p); return ; } */ ipheader = (struct ip *) p->accessdata(); if (in_cksum((unsigned short *) ipheader, (ipheader->ip_hl * 4))) { fprintf(stderr, "TCPTapAgent(%s): packet received with invalid IP checksum.\n", name()); drop(p); return; } ttl = ipheader->ip_ttl; if (!(--ttl)) { fprintf(stderr, "TCPTapAgent(%s): packet received with ttl zero.\n", name()); drop(p); return; } buf = p->accessdata(); tcpheader = (struct tcphdr *) (buf + (ipheader->ip_hl * 4)); Packet *nspacket = allocpkt(); hdr_ip *ns_iphdr = HDR_IP(nspacket); ns_iphdr->ttl() = ttl; hdr_tcp *ns_tcphdr = HDR_TCP(nspacket); #ifndef LINUX_TCP_HEADER ns_tcphdr->seqno() = ntohl(tcpheader->th_seq); ns_tcphdr->ackno() = ntohl(tcpheader->th_ack); ns_tcphdr->hlen() = (ipheader->ip_hl + tcpheader->th_off) * 4; ns_tcphdr->ts() = Scheduler::instance().clock(); ns_tcphdr->reason() |= REASON_UNKNOWN; /* Here we only propogate the "well-known" flags. If you want to add other flags, uncomment this line and comment the rest. ns_tcphdr->flags() = tcpheader->th_flags; */ ns_tcphdr->flags() = 0; if (tcpheader->th_flags & TH_FIN) ns_tcphdr->flags() |= TH_FIN; if (tcpheader->th_flags & TH_SYN) ns_tcphdr->flags() |= TH_SYN; if (tcpheader->th_flags & TH_RST) ns_tcphdr->flags() |= TH_RST; if (tcpheader->th_flags & TH_PUSH) ns_tcphdr->flags() |= TH_PUSH; if (tcpheader->th_flags & TH_ACK) ns_tcphdr->flags() |= TH_ACK; if (tcpheader->th_flags & TH_URG) ns_tcphdr->flags() |= TH_URG; #else /* LINUX_TCP_HEADER */ ns_tcphdr->seqno() = ntohl(tcpheader->seq); ns_tcphdr->ackno() = ntohl(tcpheader->ack); ns_tcphdr->hlen() = (ipheader->ip_hl + tcpheader->doff) * 4; ns_tcphdr->ts() = Scheduler::instance().clock(); ns_tcphdr->reason() |= REASON_UNKNOWN; /* Here we only propogate the "well-known" flags. If you want to add other flags, uncomment this line and comment the rest. ns_tcphdr->flags() = tcpheader->th_flags; */ #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 ns_tcphdr->flags() = 0; if (tcpheader->fin == 1 ) ns_tcphdr->flags() |= TH_FIN; if (tcpheader->syn == 1 ) ns_tcphdr->flags() |= TH_SYN; if (tcpheader->rst == 1 ) ns_tcphdr->flags() |= TH_RST; if (tcpheader->psh == 1) ns_tcphdr->flags() |= TH_PUSH; if (tcpheader->ack == 1) ns_tcphdr->flags() |= TH_ACK; if (tcpheader->urg == 1) ns_tcphdr->flags() |= TH_URG; #endif /* LINUX_TCP_HEADER */ hdr_cmn *ns_cmnhdr = HDR_CMN(nspacket); ns_cmnhdr->size() = ntohs(ipheader->ip_len); Packet::free(p); // inject into simulator target_->recv(nspacket); return; } /* * ns scheduler calls TapAgent::dispatch which calls recvpkt. * * recvpkt then calls the network (net_) to receive as many packets * as there are from the packet capture facility. * For every packet received through the callback, it converts to ns * FullTcp packet and injects it into the simulator by calling target_->recv * */ void TCPTapAgent::recvpkt() { if (net_->mode() != O_RDWR && net_->mode() != O_RDONLY) { fprintf(stderr, "TCPTapAgent(%s): recvpkt called while in write-only mode!\n", name()); return; } int cc = net_->recv(pkt_handler, this); if (cc <= 0) { if (cc < 0) { perror("recv"); } return; } TDEBUG4("%f: TCPTapAgent(%s): recvpkt, cc:%d\n", now(), name(), cc); // nothing to do coz pkt_handler would have called processpkt() // that would have injected packets into the simulator } /* * simulator schedules TapAgent::recv which calls sendpkt * * Grabs a ns Full TCP packet, converts it into real TCP packet * and injects onto the network using net_->send * */ int TCPTapAgent::sendpkt(Packet* p) { int byteswritten, datalen; unsigned char *packet; unsigned char received_ttl; int hlength = IP_HEADER_LEN + TCP_HEADER_LEN; struct tcphdr *tcpheader; if (net_->mode() != O_RDWR && net_->mode() != O_WRONLY) { fprintf(stderr, "TCPTapAgent(%s): sendpkt called while in read-only mode!\n", name()); return (-1); } // send packet into the live network hdr_cmn* ns_cmnhdr = HDR_CMN(p); if (net_ == NULL) { fprintf(stderr, "TCPTapAgent(%s): sendpkt attempted with NULL net\n", name()); drop(p); return (-1); } hdr_tcp* ns_tcphdr = HDR_TCP(p); hdr_ip * ns_iphdr = HDR_IP(p); received_ttl = ns_iphdr->ttl_; // Here we check if ns has sent any data in the packet. datalen = ns_cmnhdr->size() - ns_tcphdr->hlen(); packet = (unsigned char *) calloc (1, sizeof(unsigned char) * (hlength + datalen)); if (packet == NULL) { fprintf(stderr, "TCPTapAgent(%s) : Error %d allocating memory.\n", name(), errno); return (-1); } // Create real world tcp packet. ip_gen((char *)packet, (unsigned char) IPPROTO_TCP, nsnode.sin_addr, extnode.sin_addr, hlength + datalen, received_ttl); tcpheader = (struct tcphdr*) (packet + IP_HEADER_LEN); tcp_gen((char *)tcpheader, nsnode.sin_port, extnode.sin_port, p); #ifndef LINUX_TCP_HEADER tcpheader->th_sum = trans_check(IPPROTO_TCP, (char *) tcpheader, sizeof(struct tcphdr) + datalen, nsnode.sin_addr, extnode.sin_addr); #else tcpheader->check = trans_check(IPPROTO_TCP, (char *) tcpheader, sizeof(struct tcphdr) + datalen, nsnode.sin_addr, extnode.sin_addr); #endif /* Limits the packets going out to only IP + TCP header. ns will act as an ACK machine. */ byteswritten = net_->send(packet, hlength + datalen); if (byteswritten < 0) { fprintf(stderr,"TCPTapAgent(%s): sendpkt (%p, %d): %s\n", name(), p->accessdata(), ns_cmnhdr->size(), strerror(errno)); Packet::free(p); free(packet); return (-1); } free(packet); TDEBUG3("TCPTapAgent(%s): sent packet (sz: %d)\n", name(), byteswritten); return 0; }