/// <summary> /// Initializes a new instance of the Nic class using the specified /// MAC address. /// </summary> /// <param name="address">A MAC-48 address to assign to the NIC. If this /// is null, the NIC is assigned a random MAC address.</param> protected Nic(MacAddress address = null) { if (address != null) MacAddress = address; else MacAddress = new MacAddress(); Connector = new Connector(); }
/// <summary> /// Pierces the cable and wires the specified connector to at the specified position. /// </summary> /// <param name="position">The position at which to pierce the cable.</param> /// <param name="connector">The connector to wire to the cable at the pierced /// position.</param> /// <returns>A reference to the C10Base5 instance for chaining.</returns> /// <exception cref="InvalidOperationException">Another connector has already been /// installed at the specified position.</exception> /// <exception cref="InvalidOperationException">As per IEEE 802.3 specification the /// position at which the cable is pierced must be a multiple of 2.5 metres.</exception> public C10Base5 Pierce(double position, Connector connector) { if (connectors.Values.Contains(position)) throw new InvalidOperationException("The position is already taken."); if (Math.Abs(position % 2.5) > 0.1) throw new InvalidOperationException("Position must be a multiple of 2.5."); connectors.Add(connector, position); connector.Cable = this; return this; }
/// <summary> /// Initializes a new instance of the Bridge class. /// </summary> /// <param name="numPorts">The number of I/O ports.</param> /// <param name="delay">The inherent propagation delay of the bridge.</param> public Bridge(int numPorts, ulong delay) { for (var i = 0; i < numPorts; i++) { var c = new Connector(); c.SignalSense += (sender, e) => OnSignalSense(c); c.SignalCease += (sender, e) => OnSignalCease(c, sender, e); ports.Add(c); // House-keeping. InitFields(c); } }
/// <summary> /// Creates the needed entries in the various dictionaries for the /// specified connector. /// </summary> /// <param name="connector">The connector to initialize.</param> void InitFields(Connector connector) { tx.Add(connector, false); rx.Add(connector, false); retransmissionCount.Add(connector, 0); transmissionData.Add(connector, null); inputFifo.Add(connector, new CappedQueue<Frame>()); outputFifo.Add(connector, new CappedQueue<Frame>()); waitTime.Add(connector, 0); isIdle.Add(connector, true); }
/// <summary> /// Attach the specified connector to the cable at the specified position. /// </summary> /// <param name="position">The position at which to connect the connector to the /// cable.</param> /// <param name="connector">The connector to connect to the cable at the specified /// position.</param> /// <exception cref="InvalidOperationException">Another connector has already been /// installed at the specified position.</exception> /// <exception cref="InvalidOperationException">As per 10BASE2 specification the /// position at which a station is connected must be a multiple of 0.5 metres. /// </exception> public void Attach(double position, Connector connector) { if (connectors.Values.Contains(position)) throw new InvalidOperationException("The position is already taken."); if (Math.Abs(position % 0.5) > .0) throw new InvalidOperationException("Position must be a multiple of 0.5."); if (connectors.ContainsKey(connector)) throw new InvalidOperationException("Connector is already attached at " + connectors[connector] + "."); connectors.Add(connector, position); connector.Cable = this; }
/// <summary> /// Invokes the specified action for every connector within the /// collision domain, excluding those connected through the specified /// inport. /// </summary> /// <param name="inport">The port through which the signal is being /// received.</param> /// <param name="action">The action to invoke.</param> void Repeat(Connector inport, Action<Connector, ulong> action) { foreach (var c in ports) { if (!c.IsConnected || c == inport) continue; var pos = c.Cable.Connectors[c]; foreach (var pair in c.Cable.Connectors) { if (pair.Key == c) continue; var dist = Math.Abs(pos - pair.Value); var propDelayNs = (ulong) (1000000000 * (dist / c.Cable.PropagationSpeed)); action(pair.Key, Delay + propDelayNs); } } }
/// <summary> /// Initializes a new instance of the Hub class. /// </summary> /// <param name="numPorts">The number of I/O ports.</param> /// <param name="delay">The inherent propagation delay of the hub.</param> public Hub(int numPorts, ulong delay) { for (var i = 0; i < numPorts; i++) { var c = new Connector(); c.SignalSense += (sender, e) => { Repeat(c, (other, propDelay) => Simulation.AddEvent(new SignalSenseEvent(propDelay, other, sender))); }; c.SignalCease += (sender, e) => { Repeat(c, (other, propDelay) => Simulation.AddEvent(new SignalCeaseEvent(propDelay, other, e.Data, sender))); }; ports.Add(c); } }
/// <summary> /// Starts the actual data transfer. /// </summary> /// <param name="connector"></param> /// <param name="data">The data to transmit.</param> void StartTransmission(Connector connector, byte[] data) { // If the medium is no longer idle at this point, start over. if (rx[connector]) { Transmit(connector, data); return; } WritePhy("Starting transmission."); tx[connector] = true; transmissionData[connector] = data; connector.Transmit(data); }
/// <summary> /// Simulates the transmission of data from the specified source. /// </summary> /// <param name="source">The connector from which data is being transmitted.</param> /// <param name="data">The data that is being transmitted from the source.</param> public void Transmit(Connector source, byte[] data) { source.ThrowIfNull("source"); data.ThrowIfNull("data"); var position = connectors[source]; // Simulate physical frame corruption. if (HasNoise) data = DistortSignal(data); // Calculate transmission time = Size / Bitrate. var transTimeNs = (ulong) (1000000000 * (data.Length * 8 / (double) Bitrate)); // Calculate events for each device on the cable. foreach (var pair in connectors) { var distance = Math.Abs(position - pair.Value); // Propagation delay in nanoseconds. var propDelayNs = (ulong) (1000000000 * (distance / PropagationSpeed)); var deliveryTimeNs = propDelayNs + transTimeNs; Simulation.AddEvent( new SignalSenseEvent(propDelayNs, pair.Key, source)); Simulation.AddEvent( new SignalCeaseEvent(deliveryTimeNs, pair.Key, data, source)); } }
/// <summary> /// Simulates the transmission of a Jam signal. /// </summary> /// <param name="source">The source sending the Jam signal.</param> /// <returns>The time it takes to transmit the Jam signal, in nanoseconds.</returns> public ulong Jam(Connector source) { Simulation.RemoveEvents(ev => ev is SignalCeaseEvent && ev.Sender == source); var position = connectors[source]; // Calculate the transmission time. var transTimeNs = (ulong) (1000000000 * (48 / (double) Bitrate)); // Calculate the propagation delay and delivery time for each // NIC within the collision domain. foreach (var pair in connectors) { var distance = Math.Abs(position - pair.Value); var propDelayNs = (ulong) (1000000000 * (distance / PropagationSpeed)); var deliveryTimeNs = propDelayNs + transTimeNs; Simulation.AddEvent( new SignalCeaseEvent(deliveryTimeNs, pair.Key, null, source)); } return transTimeNs; }
/// <summary> /// Invoked om behalf of PHY whenever a frame has been transmitted. /// </summary> void OnDataTransmitted(Connector connector) { WriteMac("Finished transmitting Ethernet frame."); isIdle[connector] = true; waitTime[connector] = Simulation.Time + interframeGapTime(connector); }
/// <summary> /// Invoked on behalf of PHY whenever new data has been received. /// </summary> /// <param name="connector"></param> /// <param name="data">The data that was received.</param> void OnDataReceived(Connector connector, byte[] data) { data.ThrowIfNull("data"); WriteMac("Received an Ethernet frame."); waitTime[connector] = Simulation.Time + interframeGapTime(connector); var frame = Frame.Deserialize(data); // Compute checksum and compare to the one contained in the frame. var fcs = Frame.ComputeCheckSequence(frame); if (fcs != frame.CheckSequence) { WriteMac("Detected a bad frame check sequence, discarding."); return; } // Remember the port through which the frame came in. forwardTable[frame.Source] = connector; // If we know the destination and it's on the same port as the // source, discard the frame. if (forwardTable.ContainsKey(frame.Destination)) { if (forwardTable[frame.Source] == forwardTable[frame.Destination]) return; } // Start emptying the FIFO, if we're not already doing it. if (emptyingFifos == false) Simulation.Callback(0, EmptyFifos); // Enqueue the data. inputFifo[connector].Enqueue(frame); }
/// <summary> /// Performs the exponential backoff algorithm of CSMA/CD. /// </summary> /// <param name="connector"></param> /// <param name="deltaTime"></param> void ExponentialBackoff(Connector connector, ulong deltaTime) { retransmissionCount[connector]++; if (retransmissionCount[connector] >= maxRetransmissions) { AbortTransmission(connector); } else { // Wait a random number of slot-times, with a slot-time being // defined as the transmission time of 512 bits. // (Cmp, "Computer Networks", 5th Ed., A. Tanenbaum, p.285) var c = random.Next((int) Math.Pow(2, Math.Min(retransmissionCount[connector], maxExponentiation))); // (Ex., the slot-time on 10Mbps Ethernet is 51.2µsec) var slotTime = (ulong) ((512 / (double) configuredBitrate(connector)) * 1000000000); var _waitTime = (ulong) c * slotTime; WritePhy("Waiting for " + _waitTime + " (" + c + " slot times), " + retransmissionCount + ".Try, Total = " + (deltaTime + _waitTime)); Simulation.Callback(deltaTime + _waitTime, () => Transmit(connector, transmissionData[connector])); } }
int configuredBitrate(Connector connector) { return connector.Cable.Bitrate; }
/// <summary> /// Aborts a scheduled data transmission. /// </summary> void AbortTransmission(Connector connector) { WritePhy("Transmission failed! Maximum retransmission " + "threshold reached."); // Reset retransmission counter. retransmissionCount[connector] = 0; }
ulong interframeGapTime(Connector connector) { return (ulong) ((96 / (double) configuredBitrate(connector)) * 1000000000); }
/// <summary> /// Transmits the specified data. /// </summary> /// <param name="connector"></param> /// <param name="data">The data to transmit.</param> void Transmit(Connector connector, byte[] data) { // Defer transmission until medium becomes idle. if (rx[connector]) { // Poll medium about every 10µs. var timeout = (ulong) (10000 + random.Next(5000)); WritePhy("Deferring transmission, next try at " + timeout); Simulation.Callback(timeout, () => Transmit(connector, data)); } else { // Must wait another interframe-gap, before we can proceed. Simulation.Callback(interframeGapTime(connector), () => StartTransmission(connector, data)); } }
/// <summary> /// Invoked whenever the carrier signal ceases. /// </summary> /// <param name="connector">The connector which is no longer /// sensing a signal.</param> /// <param name="sender"></param> /// <param name="e"></param> void OnSignalCease(Connector connector, object sender, SignalCeaseEventArgs e) { if (tx[connector]) WritePhy("Finished transmitting bits."); rx[connector] = false; tx[connector] = false; // Hand data to MAC-layer. if (e.IsJam) { WritePhy("Receiving a jam signal."); return; } if (sender == connector) OnDataTransmitted(connector); else OnDataReceived(connector, e.Data); }
/// <summary> /// Invoked whenever the receiver senses a signal. /// </summary> /// <param name="connector">The connector sensing a signal.</param> void OnSignalSense(Connector connector) { WritePhy("Sensing a carier."); if (rx[connector] && tx[connector]) { // Collision. WritePhy("Collision detected."); var jamTime = connector.Jam(); ExponentialBackoff(connector, jamTime); } else { rx[connector] = true; } }