/* File: KRnet.c */ /* ********************************************************************* ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR Revised version of code by J.F. Kurose From the companion website for _Computer Networking: A Top-Down Approach Featuring the Internet_ by J.F. Kurose and K. W. Ross (c) 2001 by Addison Wesley Longman Based on document at (as of 02 July 2002) ********************************************************************* */ /* ********************************************************************* ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR: Version 1.4 VERSION 1.4.2 J. Blustein (tidied up a bit, added new error messages) VERSION 1.4.1 J. Blustein (tidied up a bit, added new error messages) VERSION 1.4 Li Lei (included queue.[ch]) VERSION 1.3.1 J. Blustein (tidied up a bit) VERSION 1.3 Jing Li (fixed simulation queue) VERSION 1.2.2 J. Blustein (fixed strncat n value mistake) VERSION 1.2.1 J. Blustein (using enumerated types instead of #define) VERSION 1.2 J. Blustein (using opaque types for message and packet) VERSION 1.1.1 J. Blustein (corrections) VERSION 1.1 J.F.Kurose This code should be used for PA2, unidirectional or bidirectional data transfer protocols (from A (the client) to B (the server). Bidirectional transfer of data is for extra credit and is not required). Network properties: - one way network delay averages five time units (longer if there are other messages in the channel for GBN), but can be larger - packets can be corrupted (either the header or the data portion) or lost, according to user-defined probabilities - packets will be delivered in the order in which they were sent (although some can be lost). ********************************************************************* */ #include #include #include #include "packet.h" #include "message.h" #include "KRnet.h" #include "client.h" #include "server.h" #include "queue.h" /* ***************************************************************** ***************** NETWORK EMULATION CODE STARTS BELOW *********** ***************************************************************** The code below emulates the layer 3 and below network environment: - emulates the tranmission and delivery (possibly with bit-level corruption and packet loss) of packets across the layer 3/4 interface - handles the starting/stopping of a timer, and generates timer interrupts (resulting in calling students timer handler). - generates message to be sent (passed from layer 5 to 4) THERE IS NO REASON THAT ANY STUDENT SHOULD HAVE TO READ OR UNDERSTAND THE CODE BELOW. YOU SHOULD NOT CHANGE OR REFERENCE (in your code) ANY OF THE DATA STRUCTURES OR CODE BELOW. If you're interested in how J.F. Kurose designed the emulator, you're welcome to look at the code -- but again, you should have to, and you definitely should not have to modify any of it. ********************************************************************* */ /* Global definitions */ /** * Trace Statements * **/ /* INDENT is for use with TRACE (see below) */ enum {INDENT=10}; /* TRACEOUT is where the TRACE statements should be printed */ static FILE * TRACEOUT = stdout; static int TRACE; /** * Event Queue Definitions * **/ /* possible events */ enum event_type { TIMER_INTERRUPT=1, FROM_LAYER5, FROM_LAYER3, DUMMY_EVENT }; typedef enum event_type event_t; /* item in event queue */ struct event { float evtime; /* event time */ event_t evtype; /* event type code */ entity_t eventity; /* entity in which event occurs */ packet_t pktptr; /* ptr to packet (if any) assoc w/ this event */ struct event *prev; /* event that precedes this one in queue */ struct event *next; /* event that follows this one in queue */ }; /* Global variable (for debugging and used in simulator) */ float KRtime = 0.0; /* */ /* Global variable (for swerr() to report errors) */ static char *progname; /* File scope variables (global to everything in this file) */ static float Version = 1.42; static struct event *evlist = NULL; /* the event list */ static queue_t q_client = NULL; /* message queue in client */ static queue_t q_server = NULL; /* message queue in server */ static int nsim = 0; /* number of messages from 5 to 4 so far */ static int nsimmax = 0; /* number of msgs to generate, then stop */ static float lossprob; /* probability that a packet is dropped */ static float corruptprob; /* probability that a bit in packet is flipped */ static float lambda; /* arrival rate of messages from layer 5 */ static int ntolayer3; /* number sent into layer 3 */ static int nlost; /* number lost in media */ static int ncorrupt; /* number corrupted by media*/ /** Prototype for private functions **/ static void init(const float); static void corrupt(packet_t *P); static void tracemain(struct event * eventptr); static void makemess(char message[], int maxlen, char seed); /* evQ.c */ static void generate_next_arrival(void); static void insertevent(struct event *p); /* random.c */ static void test_rand(void); static float jimsrand(void); /* timer.c */ static void starttimer(entity_t CorS, float increment); static void stoptimer(entity_t CorS); static int checktimer(entity_t CorS); /*------*/ /* main */ /* *----------------------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ int main(int argc, char *argv[]) { struct event *eventptr; packet_t thepkt = NULL; /* the current packet */ message_t msg2send = NULL; /* send to other entity */ char mess_cont[500]; /* for contents of message */ int mess_cont_size = sizeof(mess_cont)/sizeof(mess_cont[0]); progname = argv[0]; init(Version); clientInit(); serverInit(); while (1) { /* try to send msg out from msg queue */ if (!queue_empty(q_client)&& clientCanSendMorePkt()) { if (TRACE>2) { fprintf(TRACEOUT, "%*sMAINLOOP: data given to student code: ", INDENT, ""); print_msg(TRACEOUT, "\"", queue_top(q_client), "\"\n"); } clientOutput(queue_top(q_client)); free(queue_top(q_client)); queue_pop(q_client); } if (TRACE>0) { if (!queue_empty(q_client) && !clientCanSendMorePkt()) { fprintf(TRACEOUT, "%*sMAINLOOP: **warning** " "%sCanSendMorePkt() says not to send the data " "that is waiting in the %s's queue\n", INDENT, "", "client", "client"); } } if (TRACE>1) { if (queue_empty(q_client)) { fprintf(TRACEOUT, "%*sMAINLOOP: the client has no buffered packets :)\n", INDENT, ""); } else { fprintf(TRACEOUT, "%*sMAINLOOP: the client has %d buffered packets " "that cannot be sent right now\n", INDENT, "", queue_size(q_client)); } } if (!queue_empty(q_server)&& !serverCanSendMorePkt()) { if (TRACE>2) { fprintf(TRACEOUT, "%*sMAINLOOP: " "data given to student code: ", INDENT, ""); print_msg(TRACEOUT, "\"", queue_top(q_server), "\"\n"); } clientOutput(queue_top(q_server)); free(queue_top(q_server)); queue_pop(q_server); } if (TRACE>0) { if (!queue_empty(q_server) && !serverCanSendMorePkt()) { fprintf(TRACEOUT, "%*sMAINLOOP: **warning** " "%sCanSendMorePkt() says not to send the data " "that is waiting in the %s's queue\n", INDENT, "", "server", "server"); } } if (TRACE>1) { if (queue_empty(q_server)) { fprintf(TRACEOUT, "%*sMAINLOOP: the server has no buffered packets :)\n", INDENT, ""); } else { fprintf(TRACEOUT, "%*sMAINLOOP: the server has %d buffered packets " "that cannot be sent right now\n", INDENT, "", queue_size(q_server)); } } eventptr = evlist; /* get next event to simulate */ if (eventptr==NULL) break; evlist = evlist->next; /* remove this event from event list */ if (NULL != evlist) { evlist->prev=NULL; } if (TRACE>=2) { tracemain(eventptr); } KRtime = eventptr->evtime; /* update time to next event time */ switch (eventptr->evtype) { case FROM_LAYER5: if (nsim < nsimmax-1) { /* added by Jing on July 24, 2002 */ generate_next_arrival(); /* set up future arrival */ } makemess(mess_cont, mess_cont_size, nsim); msg2send = new_msg(); set_msg_data(msg2send, mess_cont); nsim++; switch(eventptr->eventity) { case Client: /*clientOutput(msg2send);*/ if (TRACE>2) { fprintf(TRACEOUT, "%*sEventQueue: Trying to add message to %s's queue\n", INDENT, "", "client"); } queue_push(q_client, msg2send); break; case Server: if (TRACE>2) { fprintf(TRACEOUT, "%*sEventQueue: Trying to add message to %s's queue\n", INDENT, "", "server"); } /*serverOutput(msg2send);*/ queue_push(q_server, msg2send); break; default: fprintf(stderr, "INTERNAL PANIC: " "Can't send message to impossible entity #%d\n", eventptr->eventity); exit(EXIT_FAILURE); } break; case FROM_LAYER3: if (NULL != (thepkt = new_pkt())) { copy_pkt(thepkt, eventptr->pktptr); } else { fprintf(stderr, "Out of memory -- line %d of file \"%s\"\n", __LINE__, __FILE__); exit(EXIT_FAILURE); } /* deliver packet by calling appropriate entity */ switch(eventptr->eventity) { case Client: clientInput(thepkt); break; case Server: serverInput(thepkt); break; default: fprintf(stderr, "INTERNAL PANIC: " "Can't deliver packet to impossible entity #%d\n", eventptr->eventity); exit(EXIT_FAILURE); } /* switch */ if (TRACE > 4) { print_pkt(TRACEOUT, " About to dispose of packet (", eventptr->pktptr, ")\n"); } /* free the memory for packet */ dispose_pkt(eventptr->pktptr); break; case TIMER_INTERRUPT: switch(eventptr->eventity) { case Client: clientTimerInterrupt(); break; case Server: serverTimerInterrupt(); break; default: fprintf(stderr, "INTERNAL PANIC: " "No timer for impossible entity #%d\n", eventptr->eventity); exit(EXIT_FAILURE); } /* switch */ break; default: fprintf(stderr, "INTERNAL PANIC: unknown event type #%d\n", eventptr->evtype); exit(EXIT_FAILURE); } /* switch (->evtype) */ free(eventptr); } /* while(1) */ printf("\n\n Simulator terminating at time %f\n" " after sending %d msgs from application layer\n", KRtime,nsim); clientEnd(); serverEnd(); /* clear messag queue */ destory_queue(q_client); destory_queue(q_server); return(EXIT_SUCCESS); } /* main() */ /*------*/ /* init */ /* *----------------------------------------------------------*/ /* initialize the simulator */ /*-----------------------------------------------------------------*/ void init(float Version) { printf("----- Stop and Wait Network Simulator Version %5.2f -------- \n\n", Version); test_rand(); printf("Enter the number of messages to simulate: "); scanf("%d",&nsimmax); printf("Enter packet loss probability [enter 0.0 for no loss]: "); scanf("%f",&lossprob); printf("Enter packet corruption probability [0.0 for no corruption]: "); scanf("%f",&corruptprob); printf("Enter average time between messages from sender's " "application layer [ > 0.0]: "); scanf("%f",&lambda); printf("Enter TRACE:"); scanf("%d",&TRACE); printf ("\n\n%s%d%s" "%s%f%s" "%s%f%s" "%s%f%s" "%s%d%s", "About to simulate ", nsimmax, " messages.\n", " Packet loss probability of ", lossprob*100.0, " percent.\n", " Packet corruption probability of ", corruptprob*100.0, " percent.\n", " Average time between messages of ", lambda, " time units.\n", " Debugging trace level is ", TRACE, "\n\n"); ntolayer3 = 0; nlost = 0; ncorrupt = 0; /* init message queue for client and server*/ q_client = new_queue(); q_server = new_queue(); KRtime=0.0; generate_next_arrival(); /* initialize event list */ } /* init() */ /*----------*/ /* makemess */ /* *------------------------------------------------------*/ /* Make message that comes from App layer */ /*-----------------------------------------------------------------*/ void makemess(char message[], /* array to hold the message */ int maxlen, /* size of message array */ char seed) { /* value to make message `random' */ int i; /* fill in msg to give with string of same letter */ for (i=0; ievtime); fprintf(TRACEOUT, " type: #%d ",E->evtype); switch(E->evtype) { case TIMER_INTERRUPT: fprintf(TRACEOUT, "(timer interrupt)"); break; case FROM_LAYER5: fprintf(TRACEOUT, "(from App layer)"); break; case FROM_LAYER3: fprintf(TRACEOUT, "(from N/W Serv. layer)"); break; case DUMMY_EVENT: fprintf(TRACEOUT, "(dummy event)"); break; default: fprintf(TRACEOUT, "(** unknown event **)"); }/* switch(->evtype) */ fprintf(TRACEOUT, " entity: #%d ", E->eventity); switch(E->eventity) { case Client: fprintf(TRACEOUT, " (Client (A))"); break; case Server: fprintf(TRACEOUT, " (Server (B))"); break; default: fprintf(TRACEOUT, " (** impossible entity **)"); } /* switch (->eventity) */ fprintf(TRACEOUT, "\n"); } /* tracemain() */ /************** EVENT HANDLING ROUTINES **************/ /* The next two functions handle the event list */ /*****************************************************/ /*------------*/ /* toNWSlayer */ /* *----------------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void toNWSlayer(entity_t CorS, /* which of Client or Server is trying to send to N/W Services Layer? */ packet_t packet) { /* the packet to be sent */ const char *proc="toNWSlayer"; packet_t mypkt; struct event *evptr; struct event *q; float lastime; ntolayer3++; /* Simulate losses */ if (jimsrand() < lossprob) { nlost++; if (TRACE>0) { fprintf(TRACEOUT, "%*sTONWSLAYER: packet being lost\n", INDENT, ""); } return; } /* Make a copy of the packet student just gave me since they may decide to do something with the packet after we return */ if (NULL != (mypkt = new_pkt())) { copy_pkt(mypkt, packet); } else { fprintf(stderr, "Out of memory -- line %d of file \"%s\"\n", __LINE__, __FILE__); exit(EXIT_FAILURE); } if (TRACE>2) { print_pkt(TRACEOUT, " TONWSLAYER: ", mypkt, "\n"); } /* Create future event for arrival of packet at the other side */ evptr = malloc(sizeof(struct event)); if (NULL == evptr) { swerr(proc, 1, "out of memory from malloc()"); } evptr->evtype = FROM_LAYER3; /* packet will pop out from layer3 */ /* event occurs at other entity */ evptr->eventity = (Client==CorS)?Server:Client; evptr->pktptr = mypkt; /* save ptr to my copy of packet */ /* Compute the arrival time of packet at the other end. The medium cannot reorder, so make sure packet arrives between 1 and 10 time units after the latest arrival time of packets currently in the medium on their way to the destination */ lastime = KRtime; for (q=evlist; q; q=q->next) { if ( (FROM_LAYER3==q->evtype && evptr->eventity==q->eventity) ) { lastime = q->evtime; } } evptr->evtime = lastime + 1 + 9*jimsrand(); /* simulate corruption: */ if ((jimsrand()) < corruptprob) { if (TRACE>0) { fprintf(TRACEOUT, "%*sTONWSLAYER: packet being corrupted\n", INDENT, ""); } ncorrupt++; corrupt(&mypkt); } if (TRACE>2) { fprintf(TRACEOUT, "%*sTONWSLAYER: scheduling arrival on other side\n", INDENT, ""); } insertevent(evptr); } /* toNWSlayer() */ /*---------*/ /* corrupt */ /* *-------------------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void corrupt(packet_t *P) { float rand_value = jimsrand(); if (2==TRACE) { fprintf(TRACEOUT, "%*sCORRUPT: ", INDENT+2, ""); } else if (TRACE>2) { fprintf(TRACEOUT, "%*sCORRUPT: (r == %4.3f) ", INDENT+2, "", rand_value); } /* 75% of corruptions will be in payload */ if (rand_value < .75) { if (TRACE>=2) { fprintf(TRACEOUT, "corrupted payload field. "); fprintf(TRACEOUT, "It was \"%s\" ", pkt_data(*P)); } set_pkt_data(*P, pkt_seq(*P), pkt_ack(*P), pkt_check(*P), ""); if (TRACE>=2) { fprintf(TRACEOUT, " and now is \"%s\".\n", pkt_data(*P)); } return; } /* 12.5% of corruptions will be in sequence numbers */ if (rand_value < .875) { if (TRACE>=2) { fprintf(TRACEOUT, "corrupted sequence number field. "); fprintf(TRACEOUT, "It was %d ", pkt_seq(*P)); } set_pkt_data(*P, pkt_seq(*P)%5 + 3, pkt_ack(*P), pkt_check(*P), pkt_data(*P)); if (TRACE>=2) { fprintf(TRACEOUT, " and now is %d.\n", pkt_seq(*P)); } return; } /* remaining corruptions will be in ACK number */ if (TRACE>=2) { fprintf(TRACEOUT, "corrupted ACK field. "); fprintf(TRACEOUT, "It was %d ", pkt_ack(*P)); } set_pkt_data(*P, pkt_seq(*P), pkt_ack(*P)%8 + 2, pkt_check(*P), pkt_data(*P)); if (TRACE>=2) { fprintf(TRACEOUT, " and now is %d.\n", pkt_ack(*P)); } } /* corrupt() */ /*------------*/ /* toAppLayer */ /* *----------------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void toAppLayer(entity_t CorS, char datasent[]) { if (TRACE>2) { fprintf(TRACEOUT, "%*sTOAPPLAYER: data received: ", INDENT, ""); fprintf(TRACEOUT, "%s\n", datasent); } } /* toAppLayer() */ /*************************/ /* Event Queue Functions */ /*************************/ #include "evQ.c" /*******************/ /* Timer Functions */ /*******************/ #include "timer.c" /*------------------*/ /* startClientTimer */ /* *----------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void startClientTimer(float increment) { starttimer(Client, increment); } /* startClientTimer() */ /*-----------------*/ /* stopClientTimer */ /* *-----------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void stopClientTimer(void) { stoptimer(Client); } /* stopClientTimer() */ /*------------------*/ /* startServerTimer */ /* *----------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void startServerTimer(float increment) { starttimer(Server, increment); } /* startServerTimer() */ /*-----------------*/ /* stopServerTimer */ /* *-----------------------------------------------*/ /* */ /*-----------------------------------------------------------------*/ void stopServerTimer(void) { stoptimer(Server); } /* stopServerTimer() */ /*****************************/ /** Random Number Functions **/ /*****************************/ #include "random.c" /*********************/ /** Error Reporting **/ /*********************/ #include "swerr.c" /* EOF (KRnet.c) */