/// <returns>Main receive event</returns> public override Event ReceivePacket(Packet packet) { return () => { switch(packet.type) { case PacketType.ACK: ProcessACKPacket(packet); break; case PacketType.DATA: ProcessDataPacket(packet); break; default: // ignore routing packets break; } }; }
// On every ack, adjust the timeout // On 3DA, reset `seq_num` of the host public void ProcessAck(Packet pkt, Time current_time) { if (pkt.seq_num == 1) { // first packet AdjustTimeout(current_time - pkt.timestamp, true); } else { AdjustTimeout(current_time - pkt.timestamp, false); } if (pkt.seq_num > this.biggest_ack) { this.biggest_ack = pkt.seq_num; this.dup_cnt = 0; reset_seq = false; } else if (pkt.seq_num == this.biggest_ack) { this.dup_cnt++; if (dup_cnt == 3) { reset_seq = true; } else { reset_seq = false; } } }
// On every timeout, reset `seq_num` of the host public void ProcessTimeout(Packet pkt, Time current_time) { reset_seq = true; }
// On timeout, set window size to 1, and go into slow start // When the window size is 1, don't reset slow start thresh. (to 2) // It just means that the retransmitted packet (after 3DA) has been dropped. public void ProcessTimeout(Packet pkt, Time current_time) { if (window_size != 1.0) { slow_start_thresh = System.Math.Max(2.0, window_size / 2.0); window_size = 1.0; } else { dup_cnt = 0; } reset_seq = true; slow_start = true; Simulator.Message("SS:" + this); }
// On every ack, update avg roundtrip time and its variance. // If the sequence number of the packet is bigger than the previous, // adjust the window size accordingly to the state it is in. // Otherwise, bump up the duplicate count. // Perform fast retransmit followed by congestion avoidance once you reach 3DA. public void ProcessAck(Packet pkt, Time current_time) { if (pkt.seq_num == 1) { // first packet AdjustTimeout(current_time - pkt.timestamp, true); } else { AdjustTimeout(current_time - pkt.timestamp, false); } if (pkt.seq_num > this.biggest_ack) { this.biggest_ack = pkt.seq_num; if (dup_cnt > 2) { this.dup_cnt = 0; window_size = slow_start_thresh; // finished fast recovery // time for congestion avoidance slow_start = false; reset_seq = true; Simulator.Message(this + ": TCP mode = CA"); } else { this.dup_cnt = 0; if (slow_start) { window_size++; if (window_size > slow_start_thresh) { slow_start = false; Simulator.Message(this + ": Switching from SS to CA"); } } else { // Congestion Avoidance window_size += 1/window_size; } reset_seq = false; } } else if (pkt.seq_num == this.biggest_ack) { this.dup_cnt++; if (dup_cnt == 2) { slow_start_thresh = System.Math.Max(2.0, window_size / 2.0); window_size = 1; // effectively resends a single packet slow_start = false; reset_seq = true; Simulator.Message(this + ": FAST TRANSMIT"); } else { //window_size++; reset_seq = false; } } }
/// <summary> /// The event where the Link stores the packet in the send buffer (or discards it if the buffer is full). /// </summary> /// <remarks> /// Note: A Link always immediately either accept a packet into the queue or discards it. /// The packet will be sent asynchronously at some later time. /// </remarks> /// <param name='packet'> /// The packet to be sent along this link /// </param> public Event EnqueuePacket(Packet packet) { return () => { if (!this.is_transmitting) { TransmitPacket(packet); //Simulator.Message(name + ":transmitting " + packet); } else if (this.buffer_occupancy + packet.size < this.buffer_size) { this.buffer.Enqueue(packet); //Simulator.Message(name + ":queueing " + packet); } else { this.lStatus.dropped_packets++; Simulator.Message("Link {0}: buffer {1}/{2}; dropping {3}", this.name, this.buffer_occupancy, this.buffer_size, packet); } //Logger.LogLinkStatus(lStatus); }; }
private void TransmitPacket(Packet packet) { Debug.Assert(!this.is_transmitting, "Bug: Tried to transmit two packets simultaneously"); this.is_transmitting = true; double trans_duration = packet.size / this.rate; eqp.Add(eqp.current_time + trans_duration, this.PacketTransmissionComplete()); double arrival_time = eqp.current_time + trans_duration + prop_delay; eqp.Add(arrival_time, dest.ReceivePacket(packet)); }
/// <returns> /// Event that occurs when this Node receives a packet. /// </returns> public abstract Event ReceivePacket(Packet packet);
/// <summary> /// Event that router receives a packet. /// The router will immediately perform a routing-table lookup, and forward the packet along the appropriate link. /// </summary> /// <exception cref="InvalidOperationException"> /// Throws an exception if the routing table has not been built yet. /// You should execute the RecalculateRoutingTableEvent event first. /// </exception> public override Event ReceivePacket(Packet packet) { return () => { if(packet.type == PacketType.LINK_STATE_ADVERTISEMENT) this.ProcessLinkStateAdvertisement(packet); else this.ForwardOrdinaryPacket(packet); }; }
/// <summary> /// Sends a new packet on all outgoing links. /// If during this round, an identical packet has already been sent, does nothing. /// This avoids sending infinite loops of link-state packets. /// </summary> public void Send(Packet pkt) { if(!already_seen.Contains(pkt)) { // Simulator.Message("Sending link-state packet {0} on links {1}", pkt, outgoing_links.ToDelimitedString()); foreach(Link l in outgoing_links) eqp.Add(eqp.current_time, l.EnqueuePacket(pkt)); already_seen.Add(pkt); } }
/// <summary> /// Processes a received link-state advertisement by adding it to the table of known link costs. /// If the received packet gives us enough information, this triggers recalculation of the routing table. /// The advertisement will be forwarded on all outgoing links (if not already done). /// </summary> private void ProcessLinkStateAdvertisement(Packet pkt) { // Simulator.Message("Router {0} received link-state packet for link {1}", this.ip, pkt); Debug.Assert(pkt.seq_num <= this.routing_seq_num); if(pkt.seq_num == this.routing_seq_num) { // packet describes current round Link described_link = Simulator.LinksBySrcDest[Tuple.Create(Simulator.Nodes[pkt.src], Simulator.Nodes[pkt.link_dest])]; Debug.Assert(described_link.cost == pkt.link_cost, "Sanity check failed: received link-state packet claiming incorrect link cost"); if(known_link_costs.ContainsKey(described_link)) { Debug.Assert(known_link_costs[described_link] == pkt.link_cost); } else { known_link_costs.Add(described_link, pkt.link_cost); } RecalculateRoutingTableIfEnoughInfo(); link_state_sender.Send(pkt); } else { Simulator.Message("Warning: received a link-state advertisement from a previous round {0} (current routing seq num = {1})", pkt.seq_num, this.routing_seq_num); } }
/// <summary> /// Immediately performs a routing-table lookup and forwards an ordinary packet on the appropriate link. /// </summary> private void ForwardOrdinaryPacket(Packet packet) { if(routing_table == null) throw new InvalidOperationException("Cannot route packet before routing table is calculated: " + packet); //Simulator.Message(packet); Node next = routing_table[Simulator.Nodes[packet.dest]]; Link to_next = Simulator.LinksBySrcDest[Tuple.Create((Node)this, next)]; ///Simulator.Message(ip + ":sending " + to_next + packet); eqp.Add(eqp.current_time, to_next.EnqueuePacket(packet)); }
/// Sends a packet and registers timeout private void _SendPacket() { var packet = new Packet{ payload_size=Packet.DEFAULT_PAYLOAD_SIZE, src=this.ip, dest=this.dest_ip, type=PacketType.DATA, seq_num=this.next_seq_num, timestamp = eqp.current_time }; double completion_time = eqp.current_time + packet.size / link.rate; eqp.Add(eqp.current_time, link.EnqueuePacket(packet)); is_busy = true; this.next_seq_num += 1; eqp.Add(completion_time, CompleteSend()); eqp.Add(completion_time + this.timeout, CheckTimeout(packet, window_resets)); hStat.flows[0].time = eqp.current_time; hStat.flows[0].window_size = window_size; hStat.flows[0].packets_sent++; //Logger.LogHostStatus(hStat); }
/// return ack packet for a data packet private void ProcessDataPacket(Packet packet) { if (packet.seq_num == expected_seq_num) { expected_seq_num++; } var ack_p = new Packet{payload_size=Packet.DEFAULT_ACK_SIZE, src=this.ip, dest=packet.src, type=PacketType.ACK, seq_num=expected_seq_num, timestamp=packet.timestamp}; // Simulator.Message(name+":"+eqp.current_time+": Sending " + ack_p); UpdatePacketDelay(eqp.current_time - packet.timestamp); eqp.Add(eqp.current_time, link.EnqueuePacket(ack_p)); this.flow_rec_stat.received_packets++; }
/// Process ack packet if it lies on unacknowledged window private void ProcessACKPacket(Packet packet) { if (packet.seq_num >= ack_num && packet.seq_num <= next_seq_num) { this.tcp_strat.ProcessAck(packet, eqp.current_time); UpdateTCPState(); if (this.tcp_strat.ResetSeq()) { window_resets++; } } else { Simulator.Message("Host {0} dropping ack {1}", this.ip, packet); } eqp.Add(eqp.current_time, SendPacket()); }
/// If the packet lies inside the unacknowledged window, process the timeout private Event CheckTimeout(Packet packet, int resets) { return () => { if (packet.seq_num >= ack_num && packet.seq_num < next_seq_num && window_resets == resets) { // timed out Simulator.Message("Host {0}: packet timed out: {1}", this.ip, packet); window_resets++; this.tcp_strat.ProcessTimeout(packet, eqp.current_time); UpdateTCPState(); eqp.Add(eqp.current_time, SendPacket()); } }; }