#include #include #include #include /* This is an implementation of a stop-and-wait data link protocol. It is based on Tanenbaum's `protocol 4', 2nd edition, p227. This protocol employs only data and acknowledgement frames - piggybacking and negative acknowledgements are not used. It is currently written so that only one node (number 0) will generate and transmit messages and the other (number 1) will receive them. This restriction seems to best demonstrate the protocol to those unfamiliar with it. The restriction can easily be removed by "commenting out" the line if(OS->nodenumber == 0) in reboot_node(). Both nodes will then transmit and receive (why?). Note that this file only provides a reliable data-link layer for a network of 2 nodes. */ typedef enum { DL_DATA, DL_ACK } FRAMEKIND; typedef struct { char data[MAX_MESSAGE_SIZE]; } MSG; typedef struct { FRAMEKIND kind; // only ever DL_DATA or DL_ACK size_t len; // the length of the msg field only int checksum; // checksum of the whole frame int seq; // only ever 0 or 1 MSG msg; } FRAME; #define FRAME_HEADER_SIZE (sizeof(FRAME) - sizeof(MSG)) #define FRAME_SIZE(f) (FRAME_HEADER_SIZE + f.len) MSG *lastmsg; size_t lastlength = 0; CnetTimerID lasttimer = NULLTIMER; int ackexpected = 0; int nextdatatosend = 0; int dataexpected = 0; void transmit_frame(MSG *msg, FRAMEKIND kind, size_t length, int seqno) { FRAME f; int link = 1; f.kind = kind; f.seq = seqno; f.checksum = 0; f.len = length; switch (kind) { case DL_ACK : printf("ACK transmitted, seq=%d\n", seqno); break; case DL_DATA: { CnetTime timeout; printf(" DATA transmitted, seq=%d\n", seqno); memcpy(&f.msg, msg, (int)length); timeout = FRAME_SIZE(f)*((CnetTime)8000000 / OS->links[link].bandwidth) + OS->links[link].propagationdelay; lasttimer = CNET_start_timer(EV_TIMER1, 3 * timeout, 0); break; } } length = FRAME_SIZE(f); f.checksum = CNET_ccitt((unsigned char *)&f, (int)length); CHECK(CNET_write_physical(link, &f, &length)); } EVENT_HANDLER(application_ready) { CnetAddr destaddr; lastlength = sizeof(MSG); CHECK(CNET_read_application(&destaddr, lastmsg, &lastlength)); CNET_disable_application(ALLNODES); printf("down from application, seq=%d\n", nextdatatosend); transmit_frame(lastmsg, DL_DATA, lastlength, nextdatatosend); nextdatatosend = 1-nextdatatosend; } EVENT_HANDLER(physical_ready) { FRAME f; size_t len; int link, checksum; len = sizeof(FRAME); CHECK(CNET_read_physical(&link, &f, &len)); checksum = f.checksum; f.checksum = 0; if(CNET_ccitt((unsigned char *)&f, (int)len) != checksum) { printf("\t\t\t\tBAD checksum - frame ignored\n"); return; // bad checksum, ignore frame } switch (f.kind) { case DL_ACK : if(f.seq == ackexpected) { printf("\t\t\t\tACK received, seq=%d\n", f.seq); CNET_stop_timer(lasttimer); ackexpected = 1-ackexpected; CNET_enable_application(ALLNODES); } break; case DL_DATA : printf("\t\t\t\tDATA received, seq=%d, ", f.seq); if(f.seq == dataexpected) { printf("up to application\n"); len = f.len; CHECK(CNET_write_application(&f.msg, &len)); dataexpected = 1-dataexpected; } else printf("ignored\n"); transmit_frame(NULL, DL_ACK, 0, f.seq); break; } } EVENT_HANDLER(timeouts) { printf("timeout, seq=%d\n", ackexpected); transmit_frame(lastmsg, DL_DATA, lastlength, ackexpected); } EVENT_HANDLER(showstate) { printf( "\n\tackexpected\t= %d\n\tnextdatatosend\t= %d\n\tdataexpected\t= %d\n", ackexpected, nextdatatosend, dataexpected); } EVENT_HANDLER(reboot_node) { if(OS->nodenumber > 1) { fprintf(stderr,"This is not a 2-node network!\n"); exit(1); } lastmsg = calloc(1, sizeof(MSG)); CHECK(CNET_set_handler( EV_APPLICATIONREADY, application_ready, 0)); CHECK(CNET_set_handler( EV_PHYSICALREADY, physical_ready, 0)); CHECK(CNET_set_handler( EV_TIMER1, timeouts, 0)); CHECK(CNET_set_handler( EV_DEBUG0, showstate, 0)); CHECK(CNET_set_debug_string( EV_DEBUG0, "State")); if(OS->nodenumber == 0) { CNET_enable_application(ALLNODES); } }