/* Implementation exercise 4: Error control An Engineering Approach to Computer Networking S. Keshav */ /* * This is the template for the exercise on error control. * XXX marks the places you have to fill. */ #include "../kernel/real.h" #include #include #include #include #include #define MWS 100 /* Max error control window size */ #define ALPHA 0.75 /* for round trip time estimation */ #define PKT_DATA_SIZE 250 extern int check_hamming(); /* hamming code definition */ unsigned char hamming[16] = { 0x0, 0xd2, 0x54, 0x86, 0x98, 0x4a, 0xcc, 0x1e, 0xe0, 0x32, 0xb4, 0x66, 0x78, 0xaa, 0x2c, 0xfe}; ecn_error() { PKT_PTR pkt, p; PKT_PTR save_pkt, timeout_pkt; int node, num_pkts_sent = 0, ack = 0, line_busy = 0; ident destn, sender, sink; long key; timev now, diff; int read_file, write_file; int seq_no = 0; int last_ack = -1; /* largest cumulative ack seen so * far */ char seen[MWS]; /* bit map of pkts in seq. */ PKT_PTR save[MWS]; /* saves transmitted packets */ unsigned char *r_save[MWS]; /* saves received packets */ int r_save_size[MWS]; /* size of saved packets */ int last_in_seq = -1; /* last in sequence packet at rcv */ int last_sent = -1; /* largest sequence number sent */ int final_ack = -1; /* ack for the last data pkt sent */ char no_more_pkts = 0; /* true of no more data to send */ char corrupted; unsigned char buffer[PKT_DATA_SIZE]; /* temporary storage */ unsigned char data, *r_buf; float tao, rtt_est = 1.0; float timeout = 1.0; int i, num, size; struct stat buf; node = get_node_id(); /* find out local id */ now = runtime(); source_node_type[node] = TEMPLATE; sink = assigned_sink[node]; /* find out sink (from .l file ) */ abs_advance(node_start_time[node]); /* advance time to start time */ printf("Tsource: %d ", get_node_id()); printf("--> %d, start (%d, %d)\n", sink, node_start_time[node].tv_sec, node_start_time[node].tv_usec); /* XXX -- initialize array 'seen' here */ /* open files for reading or writing */ if (node is 1) { if ((read_file = open("../sources/ecn_flow.c", O_RDONLY)) is - 1) { perror("open for read"); pr_error("error opening file for read"); } } else { /* if output file exists, first delete it */ if (stat("../tnew", &buf) isnt - 1) unlink("../tnew"); if ((write_file = open("../tnew", O_RDWR | O_CREAT, 777)) is - 1) { perror("open for write"); pr_error("error opening file for write"); } } goto test; for (ever) { recv: sender = recvm(&destn, &key, &pkt); now = runtime(); switch (pkt->type) { case ACK: /* on an ack, update notion of last_ack, and free saved packets */ printf("%f:Node %d got an ack, up to %d\n", make_float(now), node, pkt->seq_no); /* XXX The code after "test:" saves packets that should be freed here */ last_ack = pkt->seq_no; /* cumulative ack */ /* if something in retx. queue can be removed, do so */ num = num_in_q(node); for(i = 0; i < num; i++) { p = deq(node); if(p->seq_no > last_ack) enq(node, p); else free(p); } /* compute round trip time */ /* XXX Recall that pkt->gen_time has the generation time in a timeval structure. Subtract the time now from this time to find out the RTT */ /* update RTT and timeout estimate */ /* XXX Use exponential averaging (page 384 of the book), ALPHA is already defined */ /* if this is the last ack, send NO_MORE_DATA */ if(no_more_pkts and last_ack is final_ack) { pkt->type = NO_MORE_DATA; pkt->dest = 2; pkt->source = 1; enq(node, pkt); goto test; } else free(pkt); goto test; case DATA: /* receiver got data */ printf("%f:Node %d got data, seq %d\n", make_float(now), node, pkt->seq_no); /* only accept packets not already received */ /* XXX What is the condition that the packet received is not a duplicate? */ if(/*XXX*/ 1) { /* check for corrupted data */ corrupted = 0; r_buf = (unsigned char*) malloc(PKT_DATA_SIZE); for (i = 0; i < pkt->size; i += 2) { /* XXX Call check_hamming here and test for an error return */ if (/* XXX */ 1) r_buf[i/2] = (data << 4); /* since check_hamming returns nibbles ... */ else { corrupted = 1; break; } /* XXX Call check_hamming here */ if (/*XXX */ 1) r_buf[i/2] |= data; /* this is the other half of the nibble */ else { corrupted = 1; break; } } if (!corrupted) { /* save the data in a buffer */ r_save[pkt->seq_no % MWS] = r_buf; r_save_size[pkt->seq_no % MWS] = pkt->size/2; if (pkt->seq_no > last_in_seq) /* * setting the sequence number - should be the last in * sequence packet seen. Also, write out data. */ { int ack; seen[pkt->seq_no % MWS] = 1; ack = last_in_seq; while (seen[(ack + 1) % MWS]) { last_in_seq = ack; ack++; write(write_file, r_save[ack % MWS], r_save_size[ack % MWS]); free(r_save[ack%MWS]); seen[ack % MWS] = 0; } last_in_seq = ack; } /* send an ack by changing the packet's params */ pkt->seq_no = last_in_seq; pkt->type = ACK; pkt->size = 0; pkt->source = node; pkt->dest = 1; printf(" sent ack, last_in_seq %d\n", pkt->seq_no); /* assume line to source is always free */ sendm(1, 0, pkt); line_busy = 1; } else { printf(" >>packet corrupted, and cant be fixed\n"); free(pkt); free(r_buf); } } else free(pkt); goto recv; case NO_MORE_DATA: /* receiver returns the packet */ if(node is 2){ close(write_file); printf("Receiver: file transferred successfully\n"); pkt->source = 2; pkt->dest = 1; sendm(1,0,pkt); } else { /* sender terminates */ close(read_file); printf("Sender: file transferred successfully\n"); fflush(stdout); exit(); } goto recv; case INT: line_busy = 0; free(pkt); goto test; case TIMEOUT: /* Assume per-pkt timeout. On timeout, enqueue pkt. for * retransmission, in high part of retx. queue */ /* Enqueue the timeout out packet if it has not been correctly received */ if(/*XXX*/ 1) { printf("%f: node %d, enqueued seq %d on timeout\n", make_float(now), node, pkt->seq_no); enq_high(node, save[(pkt->seq_no)%MWS]); /* backoff timeout */ timeout = 2*timeout; } goto test; default: free(pkt); pr_error("source received a pkt of unknown type"); } } test: if (node is 1) { num = num_in_q(node); /* if can send data, read it into a packet */ if (num is 0 and not no_more_pkts) { size = read(read_file, buffer, PKT_DATA_SIZE); if (size > 0) { pkt = (PKT_PTR) malloc(sizeof(PKT)); pkt->type = DATA; pkt->dest = 2; pkt->source = 1; pkt->seq_no = seq_no ++; /* encode with hamming code */ for (i = 0; i < size; i++) { /*XXX Convert the bytes in buffer to encoded bytes in packet. Remember that hamming[] operates in 4 bit quantities */ } pkt->size = 2 * size; /* if last piece of data, set flag */ if(size < PKT_DATA_SIZE){ final_ack = seq_no - 1; no_more_pkts = 1; } enq(node, pkt); } if (size < 0) pr_error("error on read"); /* previous packet was last */ if (pkt->size is 0){ final_ack = seq_no - 1; no_more_pkts = 1; } } num = num_in_q(node); /* send packet if line not busy, and within error * control window */ if (not line_busy) if (num and (last_sent - last_ack) <= MWS) { pkt = deq(node); /* save a copy before transmission */ /* XXX Before you send the packet, save a copy in save[]. Remember, * just copying the pointer isnt enough: you have to copy the packet. * Use structure copy to make this easy */ pkt->gen_time = now; /* update last_sent */ if (pkt->seq_no > last_sent) last_sent = pkt->seq_no; line_busy = 1; /* set a timeout for this packet */ timeout_pkt = (PKT_PTR) malloc(sizeof(PKT)); timeout_pkt->seq_no = pkt->seq_no; timeout_pkt->type = TIMEOUT; timeout_pkt->source = timeout_pkt->dest = 1; set_timer(timeout, timeout_pkt); printf("%f: Node %d sent pkt, seq_no %d\n", make_float(now), node, pkt->seq_no); sendm(2,0,pkt); } } goto recv; } /* given a codeword byte, returns 4 bit data */ /* returns -1 if uncorrectable error, otherwise the corrected data */ int check_hamming(code, data) unsigned char code, *data; { unsigned char b1, b2, b3, b4, b5, b6, b7; unsigned char syndrome, sum; int i, ret; /* extract each bit */ /*XXX How can you get the bits in b1... b7 */ /* check parities and compute syndrome */ syndrome = 0; sum = b1 + b3 + b5 + b7; if (not(sum is 0 or sum is 2 or sum is 4)) syndrome |= 0x1; sum = b2 + b3 + b6 + b7; if (not(sum is 0 or sum is 2 or sum is 4)) syndrome |= 0x2; sum = b4 + b5 + b6 + b7; if (not(sum is 0 or sum is 2 or sum is 4)) syndrome |= 0x4; /* flip the bit indicated by syndrome */ if(syndrome) code = code ^ (0x80 >> (syndrome - 1)); /* look up the correct codeword */ ret = -1; for (i = 0; i < 16; i++) { /*XXX Match the code seen to the entry in hamming[]. If no entry is found, then we could not correct the error, return value is set to -1 */ } return ret; }