/* * Boston University * CS 555 * Fall 2003 * * Implementation of an augmented SRMP receiver. * This module implements the receiver-side of the * protocol and dumps the contents of the connection to * a file called "OutputFile" in the current directory. * * It also simulates network misbehavior by dropping packets, * dropping ACKs, and swapping some packets so that they arrive out of order. * * The probability of the errors can be changed by adjusting #defined constants * PLP, ALP, OOP below. * * Version 1.0 */ #include #include #include #include /*#include */ #include #include #include #include #include "srmp.h" #define RCVR_MAXWIN 5000 /* feel free to modify */ #define RAND_MAX 2147483647 /* = 2^31-1 */ #define PLP .0 /* packet loss probability */ #define ALP .0 /* ack loss probability */ #define OOP .2 /* probability of an out-of-order arrival */ /* See the implementation of srmp_recv_ctrl_blk in srmp.h */ /* Global file descriptor for the output file. */ int outFile; /*******************************************************************/ /* Since the protocol SRMP is event driven, we define */ /* a structure srmp_event to describe the event coming */ /* in, which enables a state transition. All events */ /* are in the form of packets from our peer. */ /******************************************************************/ typedef struct srmp_event_t { char *pkt; /*Pointer to the packet from peer */ int len; /*The length of the packet */ }srmp_event; //************************************************************** /* Return 1 with probability p, and 0 otherwise */ int event_happens(double p) { long val = lrand48(); if (val < RAND_MAX * p) { return 1; } else { return 0; } } /* * Open a UDP connection. */ int udp_open(char *remote_IP_str, int remote_port, int local_port) { int fd; uint32_t dst; struct sockaddr_in sin; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { fprintf(stderr, "Error creating unnamed socket\n"); exit(1); } /* Bind the local socket to listen at the local_port. */ printf ("Binding locally to port %d\n", local_port); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(local_port); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind"); return (-1); } /* Connect, i.e. prepare to accept UDP packets from . */ /* Listen() and accept() are not necessary with UDP connection setup. */ dst = hostname_to_ipaddr(remote_IP_str); printf ("Establishing UDP connection to <%u, port %d>\n", dst, remote_port); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(remote_port); sin.sin_addr.s_addr = dst; if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("connect"); return(-1); } return (fd); } /* * Send an SRMP ack back to the source. The srmp_CB tells * us what frame we expect, so we ack that sequence number. */ void srmp_send_ack(srmp_recv_ctrl_blk *srmp_CB) { if (!event_happens(ALP) || srmp_CB->state == SRMP_LISTEN) { /* Won't drop packets when we are sending out the ACK to acknowledge the SYN */ sendpkt(srmp_CB->fd, SRMP_ACK, srmp_CB->rwnd, srmp_CB->NBE, 0, 0); } else { printf("ACK (%u) dropped\n", srmp_CB->NBE); } } /* * Routine that simulates handing off a message to the application. * (We'll just write it to the output file directly.) * SRMP ensures that messages are delivered in sequence. */ void srmp_consume(char *pkt, int len) { char b[1000]; write(outFile, pkt, len); printf("consume: %d bytes\n", len); strncpy(b, pkt, len); b[len] = '\0'; /*printf("Contents: <%s>\n", b);*/ } int srmp_receive_state_transition_machine(srmp_recv_ctrl_blk *srmp_CB, srmp_event *pe) { unsigned short seqno; srmphdr *srh = (srmphdr *)pe->pkt; int type; unsigned short int LBA; /* Last byte accepted */ /* If the length is too short for a header, that's an error */ if (pe->len < sizeof(*srh)) { printf("Size too short.\n"); reset(srmp_CB->fd); return -1; } /* Strip out the fields of the header from the packet */ type = ntohs(srh->type); seqno = ntohs(srh->seqno); switch (srmp_CB->state ) { case SRMP_LISTEN: if (type != SRMP_SYN) { printf("Not SYN.\n"); reset(srmp_CB->fd); return -1; } srmp_CB->ISN = seqno; srmp_CB->LBRead = seqno; srmp_CB->LBReceived = seqno; srmp_CB->NBE = plus(seqno, 1); srmp_CB->state = SRMP_ESTABLISHED; srmp_send_ack(srmp_CB); return 0; break; case SRMP_TIME_WAIT: if (type != SRMP_FIN) { reset(srmp_CB->fd); return -1; } /* if the seqno of the FIN is wrong, reset the connection */ if (seqno != srmp_CB->NBE) { reset(srmp_CB->fd); return -1; } /* otherwise, ack the FIN and remain in time wait */ srmp_send_ack(srmp_CB); return 0; break; case SRMP_ESTABLISHED: /* otherwise, we're in the established state */ switch (type) { case SRMP_RESET: fprintf (stderr, "Reset received from sender -- closing\n"); sendpkt(srmp_CB->fd, SRMP_RESET, 0, 0, 0, 0); return -1; break; case SRMP_SYN: if(seqno == srmp_CB->ISN) { /* this is a retransmission of the first SYN, acknowledge */ sendpkt(srmp_CB->fd, SRMP_ACK, srmp_CB->rwnd, plus(srmp_CB->ISN, 1), 0, 0); return 0; } break; case SRMP_FIN: /* This FIN is only valid if its sequence number is NBE. */ /* Otherwise the FIN is occuring prior to our receipt of all data. */ if (seqno != srmp_CB->NBE) { printf("FIN seq not equal to NBE (%u != %u).\n", seqno, srmp_CB->NBE); reset(srmp_CB->fd); return -1; } srmp_CB->state = SRMP_TIME_WAIT; /* * TRICKY CODE ALERT: * Increment NBE in the FIN-ACK, * then decrement again in case the FIN-ACK is lost. */ srmp_CB->NBE = plus(srmp_CB->NBE, 1); srmp_send_ack(srmp_CB); srmp_CB->NBE = minus(srmp_CB->NBE, 1); return 1; /* Indicate that the file transfer is complete. Note that we do not go into the state TIME_WAIT in this implementation of the receiver. */ break; case SRMP_DATA: LBA = plus(srmp_CB->LBRead, RCVR_MAXWIN); if (greater(srmp_CB->NBE, seqno)) { /* retransmitted packet that we've already received */ /* do nothing except send an ACK (at function bottom) */ } else if(greater(seqno, LBA)) { printf("Packet seqno too large to fit in receive window.\n"); reset(srmp_CB->fd); return -1; } /* * New data has arrived. If the ACK arrives in order, * hand the data directly to the application (consume it). * and see if we've filled a gap in the sequence space. * Otherwise, stash the packet in a buffer. * In either case, send back an ACK for * the highest contiguously received packet. */ else if (seqno == srmp_CB->NBE) { pktbuf *next; unsigned short lastByte = plus(seqno, (pe->len - sizeof(*srh))); /* packet in order - send to application */ srmp_consume(pe->pkt + sizeof(*srh), pe->len - sizeof(*srh)); seqno = plus(seqno, (pe->len - sizeof(*srh))); if (greater(lastByte, srmp_CB->LBReceived)) srmp_CB->LBReceived = lastByte; /* * Now check if the arrival of this packet * allows us to consume any more packets. */ while((next = get_packet(srmp_CB, seqno)) != NULL) { printf("Batch reading!!\n"); seqno = plus(seqno,next->len); srmp_consume(next->data, next->len); free_packet(next); } srmp_CB->NBE = seqno; srmp_CB->LBRead = seqno; } else { /* * packet out of order but within receive window, * copy the data and record the seqno to validate the buffer */ unsigned short lastByte = plus(seqno, (pe->len - sizeof(*srh))); add_packet(srmp_CB, seqno, pe->len - sizeof(*srh), pe->pkt + sizeof(*srh)); if (greater(lastByte, srmp_CB->LBReceived)) srmp_CB->LBReceived = lastByte; } printf("rwnd adjusted: (%u)\n", srmp_CB->rwnd); if (minus(srmp_CB->LBReceived, srmp_CB->LBRead) > RCVR_MAXWIN) { printf("Not in feasible window.\n"); reset(srmp_CB->fd); return -1; } srmp_CB->rwnd = RCVR_MAXWIN - minus(srmp_CB->LBReceived, srmp_CB->LBRead); /* Always send an ACK back to the sender. */ srmp_send_ack(srmp_CB); return 0; break; default: /* Invalid packet received */ printf("Invalid packet.\n"); reset(srmp_CB->fd); return -1; }/*end of switch(type) in ESTABLISHED state*/ default: return -1; }/*end of switch (srmp_CB->state )*/ }/*end of srmp_receive_state_transition_machine(srmp_recv_ctrl_blk *srmp_CB, srmp_event *pe)*/ /* * Run the receiver polling loop: allocate and initialize the srmp_recv_ctrl_blk * then enter an infinite loop to process incoming packets. */ int srmp_receiver_run(char *dst, int sport, int rport) { srmp_recv_ctrl_blk *srmp_CB = (srmp_recv_ctrl_blk *)malloc(sizeof(*srmp_CB)); /* Variables used to simulate out-of-order arrivals */ int delay_pkt_set = 0; int delay_pkt_len; char delay_pkt[SRMP_MAXPKT]; srmp_event *pe = (srmp_event *)malloc(sizeof(*pe)); /* * Initialize the receiver's srmp_CB block * and open the underlying UDP/IP communication channel. */ srmp_CB->state = SRMP_LISTEN; srmp_CB->fd = udp_open(dst, sport, rport); srmp_CB->rwnd = RCVR_MAXWIN; srmp_CB->LBRead = 0; srmp_CB->LBReceived = 0; srmp_CB->NBE = 1; srmp_CB->recvQueue = NULL; /* * Enter an infinite loop reading packets from the network * and processing them. The receiver processing loop is * simple because (unlike the sender) we do not need to schedule * timers or handle any asynchronous events. */ for (;;) { int len; char pkt[SRMP_MAXPKT]; /* Block until a new packet arrives */ while ((len = readpkt(srmp_CB->fd, pkt, sizeof(pkt))) <= 0) /* Bug fixed in v1.2 */ ; /* Busy wait */ pe->pkt = pkt; pe->len = len; /* Do the processing associated with a new packet arrival */ /* But, with probability PLP, we pretend this packet got lost in the network */ if (event_happens(PLP)) { printf("PACKET DROPPED\n"); /* Do nothing */ continue; } else if (!delay_pkt_set && event_happens(OOP)) { printf("PACKET DELAYED\n"); memcpy (delay_pkt, pkt, len); delay_pkt_len = len; delay_pkt_set = 1; } else if (delay_pkt_set) { /* Process the packets in reverse order and unset delay_pkt_set bit */ if (srmp_receive_state_transition_machine(srmp_CB, pe) == -1) { return -1; } switch (srmp_receive_state_transition_machine(srmp_CB, pe)) { case -1: return -1; break; case 1: return 0; break; } delay_pkt_set = 0; } /* Otherwise, we're in normal operating mode */ else switch (srmp_receive_state_transition_machine(srmp_CB,pe)) { case -1: /* Error */ return -1; break; case 1: /* File transfer complete */ return 0; break; } } } int main(int argc, char **argv) { char dst[100]; int sport, rport; if (argc != 4) { fprintf(stderr, "usage: ReceiveApp sender-IP-address send-port recv-port\n"); exit(1); } sport = atoi(argv[2]); rport = atoi(argv[3]); strcpy (dst, argv[1]); /* * Open the output file for writing. The SRMP sender tranfers * a file to us and we simply dump it to disk. */ outFile = open("OutputFile", O_CREAT|O_WRONLY|O_TRUNC, 0644); if (outFile < 0) { perror("OutputFile"); exit(1); } /* * "Run" the receiver protocol. Application can check the return value. */ srmp_receiver_run(dst, sport, rport); return 0; }