// IPMedium.java package protocol; import java.util.*; import support.*; /** This is the class for an IP medium. @author Iain A. Robin, Kenneth J. Turner @version 1.0 (1st September 1999, IAR): initial version

1.4 (9th March 2006, KJT): updated for JDK 1.5
1.5 (24th July 2010, KJT): minor tidying */ public class IPMedium extends Medium { private int maxMessageSize = 100; // maximum message size private boolean misordering = true; // misordering allowed? private float lossRate = 0.2f; // message loss rate private Vector randomNumbers; // random numbers private int randomIndex; // random number index public IPMedium() { super(); // construct superclass randomNumbers = new Vector(); // initialise random numbers } public void initialise() { super.initialise(); randomIndex = 0; // initialise random index } public void setMaxMessageSize(int size) { maxMessageSize = size; } public void setMisordering(boolean b) { misordering = b; } public void setLossRate(float rate) { lossRate = rate; } // randomise the order of the elements of vector v private void misorder(Vector v) { Vector t = new Vector(v); int size = v.size(); v.removeAllElements(); while (size > 0) { // choose an element of t at random and add it to v int index = Math.round(--size * random()); v.addElement(t.elementAt(index)); t.removeElementAt(index); } } // Identify and return PDU currently on channel with // type and parameters matching those described in given string // Parameters are: // ( messageID [,fragment offset] [,D] [,M] ) // where D and M are booleans // "D" indicates Don't Fragment // "M" indicates More Fragments Follow protected PDU getMatchingPDU(String s) { IPMessage ipd; int messageID = -1; int offset = -1; // extract name of source entity for this datagram: int sourceStart = s.indexOf('[') + 1; int sourceEnd = s.indexOf( ']' ); String sourceName = s.substring(sourceStart, sourceEnd); // get type and parameters of PDU from action string // PDU type starts after first space following "Deliver" or "Lose": int typeStart = s.indexOf(' ') + 1; int typeEnd = s.indexOf( '(' ); if (typeEnd < 0) typeEnd = sourceStart - 2; String type = s.substring(typeStart, typeEnd); String[] params = getParams(s); messageID = Integer.parseInt(params[0]); if (params.length > 1) { // there is a fragment offset parameter try { offset = Integer.parseInt(params[1]); // fragment offset } catch (NumberFormatException e) { // not offset parameter - ignore } } // try to match IP datagram type and messageID, and fragment offset where // applicable, with a datagram currently on channel: for (Enumeration e = pdus.elements(); e.hasMoreElements(); ) { ipd = (IPMessage)e.nextElement(); if (ipd != null && ipd.type.equals(type) && ipd.getSource().getName().equals(sourceName) && messageID == ipd.messageID && (offset < 0 || (offset >= 0 && offset == ipd.fragOffset))) return(ipd); } return(null); // no match found } /** Return a list of strings describing actions (services) that can be carried out in the current state */ public Vector getServices() { Vector list = new Vector(); for (Enumeration e = pdus.elements(); e.hasMoreElements(); ) { PDU pdu = (PDU)e.nextElement(); if (pdu != null) { String s = pdu.getID(); s += " [" + pdu.getSource().getName() + "]"; if (misordering) list.addElement("Deliver " + s + " - fragmentation/misordering"); else list.addElement("Deliver " + s + " - fragmentation"); list.addElement("Lose " + s + " - congestion/error"); } } return(list); } /** Carry out the action specified in the given string, and return a list of protocol events resulting from this action */ public Vector performService(String s) { Vector events = new Vector(); IPMessage frag; IPMessage ipd = (IPMessage)getMatchingPDU(s); if (ipd == null) return(events); if (s.startsWith("Deliver")) { ProtocolEntity destination = ipd.getDestination(); // Split message into fragments (up to size maxMessageSize) int messageSize = ipd.size; for (int relOffset = 0; relOffset < messageSize; relOffset += maxMessageSize) { int size = Math.min(messageSize - relOffset, maxMessageSize); int offset = ipd.fragOffset + relOffset; frag = new IPMessage("DT", ipd.messageID, offset, size); frag.setSDU(ipd.getSDU()); frag.setMoreFrags(ipd.hasMoreFrags() || relOffset + maxMessageSize < messageSize); frag.setSource(ipd.getSource()); frag.setDestination(destination); // decide randomly whether to lose fragment if (random() >= lossRate) events.addElement(new ProtocolEvent(ProtocolEvent.FRAGMENT, frag)); else events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, frag)); } if (misordering) misorder(events); // transmit fragments in random order for (Enumeration e = events.elements(); e.hasMoreElements(); ) { ProtocolEvent event = (ProtocolEvent) e.nextElement(); if (event.getType() == ProtocolEvent.FRAGMENT) { IPMessage ipdSent = (IPMessage)event.getPDU(); transmitPDU(ipdSent, destination); } } pdus.removeElement(ipd); } if (s.startsWith("Lose")) { destinationEvents.removeAllElements(); pdus.removeElement(ipd); events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, ipd)); } // tack on any events triggered by delivery of a message // to its destination protocol entity for (Enumeration e = destinationEvents.elements(); e.hasMoreElements(); ) events.addElement((ProtocolEvent) e.nextElement()); return(events); } /** Return a random float. If the list of randoms contains an unused value then use this, otherwise generate a fresh value and add to the list. This allows random numbers to be generated consistently when simulation steps are undone/redone. @return random number in the range (0..1] */ private float random() { if (randomIndex < randomNumbers.size()) // index within list? return( // return this random ((Float)randomNumbers.elementAt(randomIndex++)).floatValue()); else { // index not within list Float random = new Float(Math.random()); // get random number randomNumbers.addElement(random); // add random to list randomIndex++; // increment list index return(random.floatValue()); // return rand number } } }