/* * PPPSim v1.0 * By Don Barnes * Created: 11.06.1999 * Updated: 11.23.1999 * Compile with Java 1.0.2 or later */ import java.awt.*; import java.util.*; import java.applet.*; import java.net.*; /* ================================================================================================================ Class: PPPSim ================================================================================================================ */ public class PPPSim extends Applet implements Runnable { // ************************************** APPLET METHODS public void init() { // ----------------- Applet size and color size = new Dimension(desiredWidth, desiredHeight); resize(size.width,size.height); // needed if, for example, we're in AppletViewer size = size(); setBackground(BGCOLOR); // ----------------- Prepare images compImageA = getImage(getDocumentBase(), "compa.gif"); compImageB = getImage(getDocumentBase(), "compb.gif"); messImage = new Image[4]; messImage[0] = getImage(getDocumentBase(), "message.gif"); messImage[1] = getImage(getDocumentBase(), "message2.gif"); messImage[2] = getImage(getDocumentBase(), "message3.gif"); messImage[3] = messImage[1]; tracker = new MediaTracker(this); tracker.addImage(compImageA,0); tracker.addImage(compImageB,1); tracker.addImage(messImage[0],2); tracker.addImage(messImage[1],3); tracker.addImage(messImage[2],4); // ----------------- Initialize simulation variables messages = new Vector(); // Initialize the peers compA = new PPPProtocol(this); compB = new PPPProtocol(this); // ----------------- Create components myCanvas = new PPPSimCanvas(new Dimension(size.width, animatedCanvasHeight), this); timerCanvas = new PPPTimerCanvas(new Dimension(size.width, timerCanvasHeight), this); pauseButton = new Button("Pause Simulation"); upButtonA = new EventButton("Up", PPPProtocol.UP, compA); downButtonA = new EventButton("Down", PPPProtocol.DOWN, compA); openButtonA = new EventButton("Open", PPPProtocol.OPEN, compA); closeButtonA = new EventButton("Close", PPPProtocol.CLOSE, compA); upButtonB = new EventButton("Up", PPPProtocol.UP, compB); downButtonB = new EventButton("Down", PPPProtocol.DOWN, compB); openButtonB = new EventButton("Open", PPPProtocol.OPEN, compB); closeButtonB = new EventButton("Close", PPPProtocol.CLOSE, compB); optionFrameButtonA = new Button("Configuration Options"); optionFrameButtonB = new Button("Configuration Options"); optionFrameA = new ChooseLCPOptionsFrame(compA, "Configuration Options for A"); optionFrameB = new ChooseLCPOptionsFrame(compB, "Configuration Options for B"); // ----------------- Initialize Applet layout GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); // rows 1 + 0 c.weightx = 0.0; c.weighty = 0.0; c.gridwidth = GridBagConstraints.REMAINDER; c.fill = GridBagConstraints.NONE; // row 0 gridbag.setConstraints(timerCanvas, c); // row 1 gridbag.setConstraints(myCanvas, c); // (applies to all following rows) c.weightx = 1.0; c.weighty = 1.0; c.fill = GridBagConstraints.BOTH; // row 2 c.gridwidth = 1; gridbag.setConstraints(upButtonA, c); gridbag.setConstraints(downButtonA, c); gridbag.setConstraints(upButtonB, c); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(downButtonB, c); // row 3 c.gridwidth = 1; gridbag.setConstraints(openButtonA, c); gridbag.setConstraints(closeButtonA, c); gridbag.setConstraints(openButtonB, c); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(closeButtonB, c); // row 4 c.gridwidth = 2; gridbag.setConstraints(optionFrameButtonA, c); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(optionFrameButtonB, c); // row 5 c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(pauseButton, c); // ----------------- Add components add(timerCanvas); add(myCanvas); add(upButtonA); add(downButtonA); add(upButtonB); add(downButtonB); add(openButtonA); add(closeButtonA); add(openButtonB); add(closeButtonB); add(optionFrameButtonA); add(optionFrameButtonB); add(pauseButton); } public void start() { if(mainThread == null) { mainThread = new Thread(this); mainThread.start(); } } public void stop() { if(mainThread != null) { mainThread.stop(); mainThread = null; } } public void paint(Graphics g) { super.paint(g); } public void update(Graphics g) { paint(g); } public boolean action(Event evt, Object what) { if(evt.target instanceof EventButton && !eventTriggered && !paused) { EventButton e = (EventButton)evt.target; eventID = e.getEvt(); eventWhat = (PPPProtocol)e.getComp(); eventTriggered = true; return true; } else if(evt.target instanceof Button) { Button e = (Button)evt.target; if(e.getLabel().equals("Pause Simulation")) { paused = true; e.setLabel("Resume Simulation"); return true; } if(e.getLabel().equals("Resume Simulation")) { paused = false; e.setLabel("Pause Simulation"); startTime = System.currentTimeMillis(); return true; } if(e == optionFrameButtonA) { optionFrameA.show(); } if(e == optionFrameButtonB) { optionFrameB.show(); } } return super.action(evt, what); } // ************************************** SIMULATION RELATED METHODS public void run() { // Let's be polite Thread.currentThread().setPriority(Thread.MIN_PRIORITY); if(!imagesLoaded) { try { tracker.waitForID(0); tracker.waitForID(1); tracker.waitForID(2); tracker.waitForID(3); tracker.waitForID(4); } catch (InterruptedException e) { return; } if(tracker.isErrorAny()) return; imagesLoaded = true; } myCanvas.updateAnimation(); myCanvas.updateStateLabels(); timerCanvas.updateStuff(compA); timerCanvas.updateStuff(compB); myCanvas.repaint(); timerCanvas.repaint(); // Get system time startTime = System.currentTimeMillis(); int i; long tempT; while(Thread.currentThread() == mainThread) { i = messages.size(); if(i > 0) { Message m; if(timeTillAnimateMessages-- <= 0) { // animate the message tiles i = messages.size(); while(i-- > 0) { m = (Message)messages.elementAt(i); m.f = ((m.f + 1) % Message.noFrames); } timeTillAnimateMessages = animateMessagesSpeed; } // move the messages down the cable i = messages.size(); while(i-- > 0) { m = (Message)messages.elementAt(i); m.x += m.v; if(m.x < 0 && m.v < 0) { compA.receive(m.packet); messages.removeElement(m); updateAll(); } if(m.x > myCanvas.cableLength && m.v > 0) { compB.receive(m.packet); messages.removeElement(m); updateAll(); } } myCanvas.updateAnimation(); // redraw the pictures on the canvas myCanvas.repaint(); } if(compA.incrementRestartTimer()) { timerCanvas.updateStuff(compA); timerCanvas.repaint(); } if(compB.incrementRestartTimer()) { timerCanvas.updateStuff(compB); timerCanvas.repaint(); } if(eventTriggered) { // user generated events eventWhat.event(eventID); eventTriggered = false; updateAll(); } if(paused) { while(paused) { Thread.yield(); } } Thread.yield(); // out of politeness try { startTime += interval; tempT = startTime - System.currentTimeMillis(); if(tempT > 0) Thread.sleep(tempT); } catch (InterruptedException e) {} } // end while loop } public void send(PPPProtocol source, PPPPacket p, int offset) { if(source == compA) { messages.addElement(new Message(0 - offset * messagePadding, 4, p, myCanvas.messFM)); } if(source == compB) { messages.addElement(new Message(myCanvas.cableLength + offset * messagePadding, -4, p, myCanvas.messFM)); } } public void updateStateLabels() { myCanvas.updateStateLabels(); myCanvas.repaint(); } public void updateAll() { timerCanvas.updateStuff(compA); timerCanvas.updateStuff(compB); timerCanvas.repaint(); myCanvas.updateAnimation(); myCanvas.repaint(); } // ************************************** DATA MEMBERS // ------------------ Instance Variables private Dimension size; // Dimensions of canvas private Thread mainThread; // components PPPSimCanvas myCanvas; PPPTimerCanvas timerCanvas; private Button pauseButton; private EventButton upButtonA; private EventButton downButtonA; private EventButton openButtonA; private EventButton closeButtonA; private Button optionFrameButtonA; private EventButton upButtonB; private EventButton downButtonB; private EventButton openButtonB; private EventButton closeButtonB; private Button optionFrameButtonB; private Frame optionFrameA; private Frame optionFrameB; // images & trackers Image compImageA; Image compImageB; Image[] messImage; private MediaTracker tracker; // simulation vars PPPProtocol compA; // computer A automaton PPPProtocol compB; // computer B automaton Vector messages; // this is the vector of packet currently on cable // misc public boolean imagesLoaded = false; private boolean paused = false; private long startTime; // used to synchronize applet speed private boolean eventTriggered = false; // true when an event button was pushed private int eventID; // set to ID of event that was triggered private PPPProtocol eventWhat; // what automaton does this event belong to // animation stuff private int timeTillAnimateMessages = animateMessagesSpeed; private final static int animateMessagesSpeed = 2; // ------------------ Class constants public static final int controlBarHeight = 160; public static final int animatedCanvasHeight = 120; public static final int timerCanvasHeight = 180; public static final int desiredHeight = timerCanvasHeight + animatedCanvasHeight + controlBarHeight; public static final int desiredWidth = 540; public static final int messagePadding = 100; // space between messages when sending 2 at a time private final static long interval = 40; // determines simulation speed (in millis per simulation cycle) public final static Color BGCOLOR = Color.pink; } /* ================================================================================================================ Class: ChooseLCPOptionsFrame ================================================================================================================ */ class ChooseLCPOptionsFrame extends Frame { public ChooseLCPOptionsFrame(PPPProtocol p, String s) { super(); comp = p; GridBagLayout gbl = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; gbc.gridheight = 1; gbc.gridwidth = GridBagConstraints.REMAINDER; // row 1 Panel p1 = new Panel(); GridBagLayout gbl1 = new GridBagLayout(); GridBagConstraints gbc1 = new GridBagConstraints(); p1.setLayout(gbl1); gbc1.weighty = 1.0; gbc1.fill = GridBagConstraints.BOTH; gbc1.weightx = 1.0; gbl1.setConstraints(MRULabel, gbc1); p1.add(MRULabel); gbc1.weightx = 4.0; gbl1.setConstraints(MRUSlider, gbc1); p1.add(MRUSlider); gbc1.weightx = 1.0; gbc1.gridwidth = GridBagConstraints.REMAINDER; gbl1.setConstraints(MRUValue, gbc1); p1.add(MRUValue); gbc.fill = GridBagConstraints.BOTH; gbl.setConstraints(p1, gbc); // row 2 gbc.fill = GridBagConstraints.NONE; gbl.setConstraints(PAPCheckbox, gbc); // row 3 Panel p3 = new Panel(); p3.add(peerID); p3.add(peerIDLabel); p3.add(peerPass); p3.add(peerPassLabel); gbl.setConstraints(p3, gbc); // row 4 Panel p4 = new Panel(); p4.add(localID); p4.add(localIDLabel); p4.add(localPass); p4.add(localPassLabel); gbl.setConstraints(p4, gbc); // last row Panel pL = new Panel(); pL.add(OKButton); pL.add(CancelButton); gbc.gridheight = GridBagConstraints.REMAINDER; gbc.anchor = GridBagConstraints.SOUTH; gbl.setConstraints(pL, gbc); setLayout(gbl); add(p1); add(PAPCheckbox); add(p3); add(p4); add(pL); setTitle(s); resize(400,300); move(300,300); } public boolean action(Event evt, Object what) { if(evt.target == OKButton || evt.target == CancelButton) { if(evt.target == OKButton) { comp.configMRU = MRUSlider.getValue() * MRUSliderFactor; if(PAPCheckbox.getState()) comp.configAuth = PPPProtocol.AUTHENTICATIONPROTOCOLPAP; else comp.configAuth = PPPProtocol.AUTHENTICATIONPROTOCOLNONE; comp.auth.localID = localID.getText(); comp.auth.localPass = localPass.getText(); comp.auth.peerID = peerID.getText(); comp.auth.peerPass = peerPass.getText(); } hide(); return true; } return super.action(evt, what); } public boolean handleEvent(Event evt) { if (evt.target == MRUSlider) { MRUValue.setText("" + MRUSlider.getValue() * MRUSliderFactor); MRUValue.repaint(); return true; } else { return super.handleEvent(evt); } } public void show() { // get current settings from comp MRUSlider.setValue(comp.configMRU / MRUSliderFactor); MRUValue.setText("" + comp.configMRU); if(comp.configAuth == PPPProtocol.AUTHENTICATIONPROTOCOLPAP) PAPCheckbox.setState(true); else PAPCheckbox.setState(false); peerID.setText(comp.auth.peerID); peerPass.setText(comp.auth.peerPass); localID.setText(comp.auth.localID); localPass.setText(comp.auth.localPass); super.show(); repaint(); } private final static int MRUSliderFactor = 4; Label MRULabel = new Label("MRU"); Scrollbar MRUSlider = new Scrollbar(Scrollbar.HORIZONTAL, PPPProtocol.defaultMRU / MRUSliderFactor, 1, 0, 2048 / MRUSliderFactor + 1); Label MRUValue = new Label("" + PPPProtocol.defaultMRU); Checkbox PAPCheckbox = new Checkbox("Require authentication (PAP)"); Label peerIDLabel = new Label("Peer ID"); TextField peerID = new TextField(8); Label peerPassLabel = new Label("Peer Pass"); TextField peerPass = new TextField(8); Label localIDLabel = new Label("Local ID"); TextField localID = new TextField(8); Label localPassLabel = new Label("Local Pass"); TextField localPass = new TextField(8); Button OKButton = new Button("OK"); Button CancelButton = new Button("CANCEL"); PPPProtocol comp; } /* ================================================================================================================ Class: EventButton Button that is bound to a specific event. ================================================================================================================ */ class EventButton extends Button { EventButton(String display, int e, PPPProtocol p) { super(display); evt = e; comp = p; } public int getEvt() { return evt; } public PPPProtocol getComp() { return comp; } private int evt; private PPPProtocol comp; } /* ================================================================================================================ Class: PPPTimerCanvas Canvas where timers and counters are displayed. ================================================================================================================ */ class PPPTimerCanvas extends Canvas { public PPPTimerCanvas(Dimension mySize, PPPSim p) { size = mySize; parent = p; titleFont = new Font("Dialog", Font.BOLD, 12); titleFM = parent.getFontMetrics(titleFont); numberFont = new Font("Dialog", Font.BOLD, 14); numberFM = parent.getFontMetrics(numberFont); numberHeight = numberFM.getHeight(); displayFont = new Font("Dialog", Font.PLAIN, 10); displayFM = parent.getFontMetrics(displayFont); } public void addNotify() { super.addNotify(); // Canvas's have an initial size of (0,0) resize(size.width,size.height); // Set up the double buffer DoubleBuffer = createImage(size.width,size.height); BufferContext = DoubleBuffer.getGraphics(); BufferContext.setColor(parent.BGCOLOR); BufferContext.fillRect(1,1,size.width - 1, size.height - 1); BufferContext.setColor(this.BGCOLOR); BufferContext.fillRoundRect(1,1,size.width / 2 - 3, size.height - 2,32,32); BufferContext.setColor(Color.black); BufferContext.drawRoundRect(1,1,size.width / 2 - 3, size.height - 2,32,32); BufferContext.setColor(this.BGCOLOR); BufferContext.fillRoundRect(size.width / 2 + 1, 1,size.width / 2 - 3, size.height - 2,32,32); BufferContext.setColor(Color.black); BufferContext.drawRoundRect(size.width / 2 + 1, 1,size.width / 2 - 3, size.height - 2,32,32); updateStuff(parent.compA); updateStuff(parent.compB); } public void paint(Graphics g) { // Simply copies the double buffer to the canvas if(DoubleBuffer != null) { g.drawImage(DoubleBuffer, 0, 0, this); } } public void update(Graphics g) { paint(g); } public void updateStuff(PPPProtocol comp) { int left, timer, counter; String tempStr, tStr, cStr; if(comp == parent.compA) { left = 0; } else { left = size.width / 2 + 1; } if(comp.getPhase() == PPPProtocol.AUTHENTICATE && comp.getState() == PPPProtocol.OPENED) { tStr = authTimerStr; cStr = authCounterStr; timer = comp.auth.getTimer(); counter = comp.auth.getCounter(); } else { tStr = timerStr; cStr = counterStr; timer = comp.getTimer(); counter = comp.getCounter(); } BufferContext.setColor(this.BGCOLOR); BufferContext.fillRect(left + 8, 4, size.width / 2 - 18, size.height - 8); BufferContext.setColor(Color.black); BufferContext.setFont(titleFont); BufferContext.drawString(tStr, left + size.width * 1/8 - titleFM.stringWidth(tStr) / 2, size.height * 3/4 ); BufferContext.drawString(cStr, left + size.width * 3/8 - titleFM.stringWidth(cStr) / 2, size.height * 3/4 ); if(timer >= 0) tempStr = "" + (timer / 10); else tempStr = new String("NR"); BufferContext.setFont(numberFont); BufferContext.drawString(tempStr, left + size.width * 1/8 - numberFM.stringWidth(tempStr) / 2, size.height * 3/4 + numberHeight); if(counter >= 0) tempStr = "" + (counter); else tempStr = new String("-"); BufferContext.setFont(numberFont); BufferContext.drawString(tempStr, left + size.width * 3/8 - numberFM.stringWidth(tempStr) / 2, size.height * 3/4 + numberHeight); if(comp.displayString.length() > 0) { BufferContext.setColor(Color.blue); FriendlyCanvas.friendlyDrawString(BufferContext, comp.displayString.toString(), left + 8, 4, size.width / 2 - 18 + left, displayFont, displayFM); } } private Image DoubleBuffer; // Image used to store double buffer private Graphics BufferContext; // Graphics context for the double buffer private Dimension size; // Dimensions of canvas private PPPSim parent; public final static Color BGCOLOR = Color.white; private Font titleFont; private FontMetrics titleFM; private Font numberFont; private FontMetrics numberFM; private int numberHeight; private Font displayFont; private FontMetrics displayFM; private final static String timerStr = "Restart Timer"; private final static String counterStr = "Restart Counter"; private final static String authTimerStr = "Retry Timer"; private final static String authCounterStr = "Retry Counter"; } /* ================================================================================================================ Class: PPPSimCanvas Canvas where animation will take place ================================================================================================================ */ class PPPSimCanvas extends Canvas { // Canvas divisions: // // State labels: (0,size.height / 2 + compHeight / 2, size.width, size.height - (size.height / 2 + compHeight / 2)) // Animation: (leftEnd - 70, middle - compHeight / 2 - 20 - 10, cableLength + 70 + 70, compHeight + 20 + 10) public PPPSimCanvas(Dimension mySize, PPPSim p) { size = mySize; leftEnd = size.width/2 - cableLength/2; rightEnd = size.width/2 + cableLength/2; middle = size.height / 2; parent = p; loadingStr = "Loading images..."; loadingFont = new Font("Helvetica", Font.BOLD, 16); loadingFM = parent.getFontMetrics(loadingFont); stateFont = new Font("Helvetica", Font.BOLD, 14); stateFM = parent.getFontMetrics(stateFont); messFont = new Font("Helvetica", Font.BOLD, 10); messFM = parent.getFontMetrics(messFont); } public void addNotify() { super.addNotify(); // Canvas's have an initial size of (0,0) resize(size.width,size.height); // Set up the double buffer DoubleBuffer = createImage(size.width,size.height); BufferContext = DoubleBuffer.getGraphics(); BufferContext.setColor(this.BGCOLOR); BufferContext.fillRect(0,0,size.width,size.height); } public void paint(Graphics g) { // Simply copies the double buffer to the canvas if(DoubleBuffer != null) { if(!parent.imagesLoaded) { g.setFont(loadingFont); g.setColor(Color.red); g.drawString(loadingStr, size.width / 2 - loadingFM.stringWidth(loadingStr) / 2, size.height / 2 + loadingFM.getHeight() / 2); } else { g.drawImage(DoubleBuffer, 0, 0, this); } } } public void update(Graphics g) { paint(g); } // redraws the picture for the animation onto the double buffer public void updateAnimation() { Message m; int x,y, i; // get the lock for this object so somebody doesn't draw it while we're modifying the buffer BufferContext.setColor(this.BGCOLOR); BufferContext.fillRect(leftEnd - 70, middle - compHeight / 2 - 20 - 10, cableLength + 70 + 70, compHeight + 20 + 10); BufferContext.setColor(Color.cyan); BufferContext.drawLine(leftEnd - 8, middle, rightEnd, middle); BufferContext.setColor(Color.blue); BufferContext.drawLine(leftEnd - 8, middle - 1, rightEnd, middle - 1); BufferContext.drawLine(leftEnd - 8, middle + 1, rightEnd, middle + 1); BufferContext.setFont(messFont); i = parent.messages.size(); while(i-- > 0) { m = (Message)parent.messages.elementAt(i); if(m.x <= cableLength && m.x >= 0) { x = m.x + leftEnd - messSize / 2; y = middle - messSize / 2; if(m.s != null){ messStr = m.s; stringLeft = x + messSize / 2 - m.stringWidth / 2; stringRight = x + messSize / 2 + m.stringWidth / 2; BufferContext.setColor(Color.red); BufferContext.drawLine(x + messSize / 2, y-3, x + messSize / 2, y-20+4); BufferContext.setColor(m.bgcolor); BufferContext.fillRoundRect(stringLeft - 4, y - 20 - m.stringHeight, m.stringWidth + 8, m.stringHeight + 3, 8, 8); BufferContext.setColor(Color.blue); BufferContext.drawRoundRect(stringLeft - 4, y - 20 - m.stringHeight, m.stringWidth + 8, m.stringHeight + 3, 8, 8); BufferContext.setColor(m.fgcolor); BufferContext.drawString(messStr, stringLeft, y - 20); } BufferContext.drawImage(parent.messImage[m.f], x, y, this); } } BufferContext.drawImage(parent.compImageA, leftEnd - compWidth + 16, middle - compHeight / 2, this); BufferContext.drawImage(parent.compImageB, rightEnd - 16 - 6, middle - compHeight / 2, this); } public void updateStateLabels() { int leftEnd = size.width/2 - cableLength/2; int rightEnd = size.width/2 + cableLength/2; int base = size.height / 2 + compHeight / 2; BufferContext.setColor(this.BGCOLOR); BufferContext.fillRect(0, base, size.width, size.height - base); BufferContext.setColor(Color.black); BufferContext.setFont(stateFont); stateString = PPPProtocol.stateNames[parent.compA.getState()]; BufferContext.drawString(stateString, leftEnd - stateFM.stringWidth(stateString) / 2 - 6, base + stateFM.getHeight()); stateString = PPPProtocol.stateNames[parent.compB.getState()]; BufferContext.drawString(stateString, rightEnd - stateFM.stringWidth(stateString) / 2, base + stateFM.getHeight()); } public boolean mouseDown(Event evt, int x, int y) { if(y > size.height / 2 + compHeight / 2 && y < size.height / 2 + compHeight / 2 + 40) { if(x > size.width/2 - cableLength/2 - 40 && x < size.width/2 - cableLength/2 + 40) { int s = parent.compA.getState(); fd.setStuff(PPPProtocol.stateNames[s] + " state", PPPProtocol.stateDescriptions[s]); fd.show(); fd.requestFocus(); return true; } if(x > size.width/2 + cableLength/2 - 40 && x < size.width/2 + cableLength/2 + 40) { int s = parent.compB.getState(); fd.setStuff(PPPProtocol.stateNames[s] + " state", PPPProtocol.stateDescriptions[s]); fd.show(); fd.requestFocus(); return true; } } return false; } private Image DoubleBuffer; // Image used to store double buffer private Graphics BufferContext; // Graphics context for the double buffer private Dimension size; // Dimensions of canvas private PPPSim parent; public final static Color BGCOLOR = Color.pink; public final static int cableLength = 360; // Length of the cable in pixels private final static int compWidth = 44; // Size of the comp images private final static int compHeight = 40; private final static int messSize = 18; private String loadingStr; private Font loadingFont; private FontMetrics loadingFM; private String stateString; private Font stateFont; private FontMetrics stateFM; private String messStr; public FontMetrics messFM; public Font messFont; private int leftEnd; private int rightEnd; private int middle; private int stringLeft; private int stringRight; private FriendlyDialog fd = new FriendlyDialog(); } /* ================================================================================================================ Class: FriendlyCanvas A simple class for making a friendly canvas. :) ================================================================================================================ */ class FriendlyCanvas extends Canvas { public FriendlyCanvas(String s, Font f, FontMetrics fm) { str = s; font = f; fontm = fm; setBackground(Color.white); } public void setText(String s) { str = s; repaint(); } public void paint(Graphics g) { g.setColor(Color.black); friendlyDrawString(g,str,2,2,size().width - 4,font, fontm); } public static void friendlyDrawString(Graphics g, String s, int left, int top, int width, Font f, FontMetrics fm) { int lineHeight = fm.getAscent() + 2; int x = left, y = top + lineHeight; String t; StringTokenizer st = new StringTokenizer(s, " ", true); g.setFont(f); while(st.hasMoreElements()) { t = st.nextToken(); if(x + fm.stringWidth(t) > width) { x = left; y += lineHeight; } if( !(x == left && t.equals(" ")) ) { g.drawString(t, x, y); x += fm.stringWidth(t); } } } public String str; public Font font; public FontMetrics fontm; } /* ================================================================================================================ Class: FriendlyDialog A simple class for making a friendly dialog. :) ================================================================================================================ */ class FriendlyDialog extends Frame { public FriendlyDialog() { super(); f = new Font("Helvetica", Font.PLAIN, 14); fm = getFontMetrics(f); c = new FriendlyCanvas("", f, fm); add("Center", c); add("South", new Button("OK")); resize(300,250); move(200,200); } public boolean action(Event evt, Object what) { if(evt.target instanceof Button && ((Button)evt.target).getLabel().equals("OK")) { hide(); return true; } return super.action(evt, what); } public boolean handleEvent(Event evt) { if(evt.id == Event.WINDOW_DESTROY) { hide(); return true; } return super.handleEvent(evt); } public void setStuff(String title, String message) { c.setText(message); setTitle(title); } private FriendlyCanvas c; public FontMetrics fm; public Font f; } /* ================================================================================================================ Class: PPPPacket Class for representing PPP packets. ================================================================================================================ */ class PPPPacket { // ************************************************************ Methods public PPPPacket(int p, Object i) { // i = object to encapsulate in a PPP packet protocolField = p; informationField = i; } public int getProtocolField() { return protocolField; } public Object getInformationField() { return informationField; } // ************************************************************ Instance Variables // PPP packet fields private int protocolField; private Object informationField; // ************************************************************ Class Constants // reserved protocol field values public final static int LINKCONTROLPROTOCOL = 0xc021; public final static int PADDINGPROTOCOL = 0x0001; public final static int PASSWORDAUTHENTICATIONPROTOCOL = 0xc023; public final static int LINKQUALITYREPORTPROTOCOL = 0xc025; public final static int CHALLENGEHANDSHAKEAUTHENTICATIONPROTOCOL = 0xc223; } /* ================================================================================================================ Class: LCPPacket Class for representing simulating Link Control Protocol packets. ================================================================================================================ */ class LCPPacket { // ************************************************************ Methods public LCPPacket(int c, int i) { code = c; identifier = i; } public int getCode() { return code; } public int getIdentifier() { return identifier; } public void addData(Object d) { data.addElement(d); } public Object dataAt(int i) throws ArrayIndexOutOfBoundsException { return data.elementAt(i); } public int length() { return data.size(); } public String toString() { return new String(codeNames[code]); } // ************************************************************ Instance Variables // packet fields private int code; // identifies type of LCP packet private int identifier; // aids in matching requests and replies private Vector data = new Vector(); // data field; dependent on code type // ************************************************************ Class Constants // code values public final static int CONFIGUREREQUEST = 1; public final static int CONFIGUREACK = 2; public final static int CONFIGURENAK = 3; public final static int CONFIGUREREJECT = 4; public final static int TERMINATEREQUEST = 5; public final static int TERMINATEACK = 6; public final static int CODEREJECT = 7; public final static int PROTOCOLREJECT = 8; public final static int ECHOREQUEST = 9; public final static int ECHOREPLY = 10; public final static int DISCARDREQUEST = 11; // code display names public final static String[] codeNames = { "ILLEGAL CODE", "Configure-Request", "Configure-Ack", "Configure-Nak", "Configure-Reject", "Terminate-Request", "Terminate-Ack", "Code-Reject", "Protocol-Reject", "Echo-Request", "Echo-Reply", "Discard-Request" }; } /* ================================================================================================================ Class: LCPConfigurationOption Class for representing LPC configuration options. ================================================================================================================ */ class LCPConfigurationOption { // ************************************************************ Methods public LCPConfigurationOption(int t) { type = t; } public int getType() { return type; } public void addData(Object d) { data.addElement(d); } public Object dataAt(int i) throws ArrayIndexOutOfBoundsException { return data.elementAt(i); } public String toString() { return new String(optionNames[type]); } // ************************************************************ Instance Variables // packet fields private int type; // identifies type of option private Vector data = new Vector(); // data field; dependent on option type // ************************************************************ Class Constants // option types public final static int MAXIMUMRECEIVEUNIT = 1; public final static int AUTHENTICATIONPROTOCOL = 3; public final static int QUALITYPROTOCOL = 4; public final static int MAGICNUMBER = 5; public final static int PROTOCOLFIELDCOMPRESSION = 7; public final static int ADDRESSANDCONTROLFIELDCOMPRESSION = 8; // option display names public final static String[] optionNames = { "?", "Maximum-Receive-Unit", "?", "Authentication-Protocol", "Quality-Protocol", "Magic-Number", "?", "Protocol-Field-Compression", "Protocol-Field-Compression", "Address-And-Control-Field-Compression" }; } /* ================================================================================================================ Class: PPPProtocol Class that simulates the PPP option negotion automaton as described in RFC 1661. ================================================================================================================ */ class PPPProtocol { // ************************************************************ Methods public PPPProtocol(PPPSim p) { parent = p; } public void receive(PPPPacket p) { // // receive(PPPPacket) // Handles the receiving of packets, and triggers the corresponding events if applicable. // Object i = p.getInformationField(); int pf = p.getProtocolField(); if(pf == PPPPacket.PASSWORDAUTHENTICATIONPROTOCOL) { if(auth != null) { auth.receive((PAPPacket)i); } else { } } if(pf == PPPPacket.LINKCONTROLPROTOCOL) { LCPPacket lcp = (LCPPacket)i; int id = lcp.getCode(); int ident = lcp.getIdentifier(); switch(id) { case(LCPPacket.CONFIGUREREQUEST): // <-------- Configure Request lastReqId = ident; // reset defaults peerAuth = AUTHENTICATIONPROTOCOLNONE; MTU = defaultMRU; // make sure contents are ok LCPConfigurationOption lco; LCPPacket l = new LCPPacket(LCPPacket.CONFIGURENAK,lastReqId); LCPPacket lrej = new LCPPacket(LCPPacket.CONFIGUREREJECT,lastReqId); for(int j = 0; j < lcp.length(); j++) { lco = (LCPConfigurationOption)lcp.dataAt(j); switch(lco.getType()) { case(LCPConfigurationOption.MAXIMUMRECEIVEUNIT): // MRU option /* if( MRU rejected ) { LCPConfigurationOption nlco = new LCPConfigurationOption(LCPConfigurationOption.MAXIMUMRECEIVEUNIT); nlco.addData( new Integer( acceptable MRU here ) ); l.addData(nlco); } else { MTU = ((Integer)lcp.dataAt(0)).intValue(); } */ MTU = ((Integer)lco.dataAt(0)).intValue(); break; case(LCPConfigurationOption.AUTHENTICATIONPROTOCOL): // Authentication option /* if( AP rejected ) { LCPConfigurationOption nlco = new LCPConfigurationOption(LCPConfigurationOption.AUTHENTICATIONPROTOCOL); lrej.addData(nlco); } else { blah blah } */ peerAuth = ((Integer)lco.dataAt(0)).intValue(); break; default: // if we don't support this code, send a reject LCPConfigurationOption nlco = new LCPConfigurationOption(id); lrej.addData(nlco); break; } } lcpNak = null; lcpRej = null; if(l.length() > 0 || lrej.length() > 0) { if(l.length() > 0) lcpNak = l; if(lrej.length() > 0) lcpRej = lrej; event(RCRm); } else { event(RCRp); } break; case(LCPPacket.CONFIGUREACK): // <-------- Configure ACK if(currentIdentifier == ident) { event(RCA); } break; case(LCPPacket.CONFIGURENAK): // <-------- Configure NAK if(currentIdentifier == ident) { event(RCN); } break; case(LCPPacket.CONFIGUREREJECT): // <-------- Configure Reject if(currentIdentifier == ident) { event(RCJ); } break; case(LCPPacket.TERMINATEREQUEST): lastReqId = ident; event(RTR); break; case(LCPPacket.TERMINATEACK): if(currentIdentifier == ident) { event(RTA); } break; case(LCPPacket.CODEREJECT): if(currentIdentifier == ident) { event(RCJp); } break; case(LCPPacket.PROTOCOLREJECT): if(currentIdentifier == ident) { event(RPJp); } break; case(LCPPacket.ECHOREQUEST): lastReqId = ident; event(RERQ); break; case(LCPPacket.ECHOREPLY): if(currentIdentifier == ident) { event(RERP); } break; case(LCPPacket.DISCARDREQUEST): event(RDR); break; default: event(RUC); // unknown code break; } } } public void event(int evt) { int prevState = currentState; int origevt = evt; if( (stateTransitionTable[evt][prevState][1] == ILLEGAL && currentState <= STARTING && origevt >= RCRp) ) return; displayString.setLength(0); displayString.append(eventDescriptions[evt]); evt = EventToSTT[evt]; // convert the event code to the correct table entry index offset = 0; currentState = stateTransitionTable[evt][currentState][STT_NEXTSTATE]; if(currentState != prevState) displayString.append(" " + nextStateDescriptions[currentState]); /* // Wasn't needed, this is better handled by the IRC action (see below) if(waitsForConfigure[currentState] && !waitsForConfigure[prevState]) restartCounter = maxConfigure; if(waitsForTerminate[currentState] && !waitsForTerminate[prevState]) restartCounter = maxTerminate; */ int a; for(int i = 1; i <= stateTransitionTable[evt][prevState][STT_NOACTIONS]; i++) { a = stateTransitionTable[evt][prevState][i]; if(a == ILLEGAL && origevt == UP) displayString.append(" This event is illegal in the current state, because the physical layer is already up."); else if(a == ILLEGAL && origevt == DOWN) displayString.append(" This event is illegal in the current state, because the physical layer is already down."); else action(a); } if(!timerRunning[currentState]) restartTimer = -1; if(currentState == prevState) displayString.append(" No change in state has occurred."); if(currentState == OPENED && currentState != prevState) { auth.init(); setPhase(AUTHENTICATE); if(peerAuth == AUTHENTICATIONPROTOCOLNONE) auth.us(true); if(configAuth == AUTHENTICATIONPROTOCOLNONE) auth.peer(true); if(!auth.authUsWithPeer) { auth.startRetryTimer(); auth.sendRequest(); displayString.append(" Now authentication must begin, and an Authenticate-Request will be sent."); } } parent.updateStateLabels(); } public void setPhase(int p) { phase = p; } public int getPhase() { return phase; } public void action(int act) { // assumes we are in the "next state" switch(act) { case(TLU): break; case(TLD): break; case(TLS): break; case(TLF): break; case(IRC): if(currentState == CLOSING || currentState == STOPPING) { displayString.append(" The Restart Counter has been initialized to the value of Max-Terminate."); restartCounter = maxTerminate; } else { displayString.append(" The Restart Counter has been initialized to the value of Max-Configure."); restartCounter = maxConfigure; } startRestartTimer(); break; case(ZRC): displayString.append(" The Restart Counter has been set to zero."); restartCounter = 0; startRestartTimer(); break; case(SCR): startRestartTimer(); currentIdentifier++; LCPPacket l = new LCPPacket(LCPPacket.CONFIGUREREQUEST,currentIdentifier); LCPConfigurationOption lcoMRU = new LCPConfigurationOption(LCPConfigurationOption.MAXIMUMRECEIVEUNIT); LCPConfigurationOption lcoAuth = new LCPConfigurationOption(LCPConfigurationOption.AUTHENTICATIONPROTOCOL); if(configMRU != defaultMRU) { lcoMRU.addData(new Integer(configMRU)); l.addData(lcoMRU); } if(configAuth != AUTHENTICATIONPROTOCOLNONE) { lcoAuth.addData(new Integer(configAuth)); l.addData(lcoAuth); } parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, l),offset); displayString.append(" A Configure-Request has been be sent."); if(l.length() == 0) displayString.append(" Since all the default values are being used, no configuration options were specified."); else { displayString.append(" It specified "); for(int j = 0; j < l.length(); j++) { if(j == l.length() - 1 && l.length() > 1) displayString.append("and "); displayString.append(LCPConfigurationOption.optionNames[ ((LCPConfigurationOption)l.dataAt(j)).getType() ]); if(j != l.length() - 1) displayString.append(", "); else displayString.append("."); } } restartCounter--; offset++; break; case(SCA): displayString.append(" A Configure-Ack has been sent."); parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, new LCPPacket(LCPPacket.CONFIGUREACK,lastReqId)),offset); offset++; break; case(SCN): if(lcpRej == null) { displayString.append(" A Configure-Nak has been sent indicating an acceptable value for each of the corresponding options."); parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, lcpNak), offset); offset++; } else { displayString.append(" A Configure-Reject has been sent indicating which options are non-negotiable."); parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, lcpRej), offset); offset++; } lcpNak = null; lcpRej = null; break; case(STR): displayString.append(" A Terminate-Request has been sent."); startRestartTimer(); currentIdentifier++; parent.send(this,new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, new LCPPacket(LCPPacket.TERMINATEREQUEST,currentIdentifier)),offset); restartCounter--; offset++; break; case(STA): displayString.append(" A Terminate-Ack has been sent."); parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, new LCPPacket(LCPPacket.TERMINATEACK, lastReqId)),offset); offset++; break; case(SCJ): displayString.append(" A Code-Reject has been sent."); parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, new LCPPacket(LCPPacket.CODEREJECT,lastReqId)),offset); offset++; break; case(SER): displayString.append(" An Echo-Request has been sent."); currentIdentifier++; parent.send(this, new PPPPacket(PPPPacket.LINKCONTROLPROTOCOL, new LCPPacket(LCPPacket.ECHOREQUEST,lastReqId)),offset); offset++; break; case(ILLEGAL): displayString.append(" This event is illegal in the current state."); break; } // end switch } public int getState() { return currentState; } public int getTimer() { return restartTimer; } public int getCounter() { return restartCounter; } public void startRestartTimer() { restartTimer = timeoutValue; } public boolean incrementRestartTimer() { if(phase == AUTHENTICATE && currentState == OPENED) return auth.incrementRestartTimer(); else { if(restartTimer > -1) { if( (--restartTimer) == 0) { if(restartCounter == 0) { event(TOm); return true; } event(TOp); return true; } } if( (restartTimer % 10) == 9) return true; return false; } } // ************************************************************ // ************************************************************ Instance Variables // ************************************************************ PPPSim parent; // Parent private int currentState = INITIAL; // Current State int currentIdentifier = 0; // Identifier int lastReqId = -1; // ID of last request recvd private int restartCounter = -1; // Counters private int maxConfigure = 10; // for Configure-Requests private int maxTerminate = 2; // for Terminate-Requests // private int maxFailure = 5; // for Configure-Nak private int restartTimer = -1; // Restart Timer private int timeoutValue = 259; // Default timeout value private int phase = LINKDEAD; PAPProtocol auth = new PAPProtocol(this); // Configuration Options specified by the administrator int configMRU = defaultMRU; int configAuth = AUTHENTICATIONPROTOCOLNONE; // peer Configuration Options int MTU; // Maximum-Transmit-Unit (equal ro peer's MRU) int peerAuth; // Authentication protocol being used by peer // MISC int offset = 0; // Used to position packets on the display LCPPacket lcpNak; LCPPacket lcpRej; StringBuffer displayString = new StringBuffer(); // ************************************************************ // ************************************************************ Class Constants // ************************************************************ // Phases public final static int LINKDEAD = 0; public final static int ESTABLISH = 1; public final static int AUTHENTICATE = 2; public final static int NETWORK = 3; public final static int TERMINATE = 4; // Options public final static int defaultMRU = 1500; // Authentication Protocols public final static int AUTHENTICATIONPROTOCOLNONE = -1; public final static int AUTHENTICATIONPROTOCOLPAP = 0xc023; public final static int AUTHENTICATIONPROTOCOLCHAP = 0xc223; // state properties private final static boolean timerRunning[] = { false, false, false, false, true, true, true, true, true, false }; // private final static boolean waitsForConfigure[] = { false, false, false, false, false, false, true, true, true, false }; // private final static boolean waitsForTerminate[] = { false, false, false, false, true, true, false, false, false, false }; // automaton events (p == +, m == -) public final static int UP = 0; public final static int DOWN = 1; public final static int OPEN = 2; public final static int CLOSE = 3; public final static int TOp = 4; public final static int TOm = 5; public final static int RCRp = 6; public final static int RCRm = 7; public final static int RCA = 8; public final static int RCN = 9; public final static int RCJ = 10; public final static int RTR = 11; public final static int RTA = 12; public final static int RUC = 13; public final static int RCJp = 14; public final static int RPJp = 15; public final static int RCJm = 16; public final static int RPJm = 17; public final static int RERQ = 18; public final static int RERP = 19; public final static int RDR = 20; // event names /* public final static String[] eventNames = { "lower layer is Up", "lower layer is Down", "administrative Open", "administrative Close", "Timeout with counter", "Timeout with counter expired", "Receive-Configure-Request (Good)", "Receive-Configure-Request (Bad)", "Receive-Configure-Ack", "Receive-Configure-Nak", "Receive-Configure-Rej", "Receive-Terminate-Request", "Receive-Terminate-Ack", "Receive-Unknown-Code", "Receive-Code-Reject (permitted)", "Receive-Protocol-Reject (permitted)", "Receive-Code-Reject (catastrophic)", "Receive-Protocol-Reject (catastrophic)", "Receive-Echo-Request", "Receive-Echo-Reply", "Receive-Discard-Request", "Illegal-Event" }; */ public final static String[] eventDescriptions = { "The physical layer indicated that it is ready to carry packets.", "The physical layer indicated that it is no longer ready to carry packets.", "The network administrator indicated that the link is allowed to be Opened.", "The network administrator indicated that the link is no longer allowed to be Opened.", "The Restart Timer expired, but the Restart Counter continues to be greater than zero.", "The Restart Timer expired, and the Restart Counter is no longer greater than zero.", "A Configure-Request packet was received from the peer, and all of the configuration options in it were acceptable.", "A Configure-Request packet was received from the peer, but it contained unacceptable configuration options.", "A Configure-Ack packet was received from the peer, indicating that all requested configuration options were acceptable.", "A Configure-Nak packet was received from the peer, indicating that there were unnacceptable options in the Configuration-Request.", "A Configure-Reject packet was received from the peer, indicating that there were options that are not acceptable for negotiation in the Configuration-Request.", "A Terminate-Request packet was received from the peer.", "A Terminate-Ack packet was received from the peer.", "An Unknown-Code packet was received from the peer.", "A permitted Code-Reject packet was received from the peer.", "A permitted Protocol-Reject packet was received from the peer.", "A catastrophic Code-Reject packet was received from the peer.", "A catastrophic Protocol-Reject packet was received from the peer.", "A Echo-Request packet was received from the peer.", "A Echo-Reply packet was received from the peer.", "A Discard-Reply packet was received from the peer." }; // automaton actions public final static int TLU = 0; public final static int TLD = 1; public final static int TLS = 2; public final static int TLF = 3; public final static int IRC = 4; public final static int ZRC = 5; public final static int SCR = 6; public final static int SCA = 7; public final static int SCN = 8; public final static int STR = 9; public final static int STA = 10; public final static int SCJ = 11; public final static int SER = 12; public final static int ILLEGAL = 255; // action names public final static String[] actionNames = { "This-Layer-Up", "This-Layer-Down", "This-Layer-Started", "This-Layer-Finished", "Initialize-Restart-Count", "Zero-Restart-Count", "Send-Configure-Request", "Send-Configure-Ack", "Send-Configure-Nak/Rej", "Send-Terminate-Request", "Send-Terminate-Ack", "Send-Code-Reject", "Send-Echo-Reply" }; // automaton states public final static int INITIAL = 0; public final static int STARTING = 1; public final static int CLOSED = 2; public final static int STOPPED = 3; public final static int CLOSING = 4; public final static int STOPPING = 5; public final static int REQSENT = 6; public final static int ACKRCVD = 7; public final static int ACKSENT = 8; public final static int OPENED = 9; // state names public final static String[] stateNames = { "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping", "Request-Sent", "Ack-received", "Ack-Sent", "Opened" }; // values for mapping Events to the STT table private final static int[] EventToSTT = { 0,1,2,3,4,5,6,7,8,9,9,10,11,12,13,13,14,14,15,15,15 }; // ------ State Transition Table ------ public final static int STT_NOACTIONS = 0; public final static int STT_ACTION1 = 1; public final static int STT_ACTION2 = 2; public final static int STT_ACTION3 = 3; public final static int STT_NEXTSTATE = 4; // The complete state transition table for simulating a PPP link private final static int[][][] stateTransitionTable = { // fields: {noActions, action1, action2, action3, nextState} /* Initial Starting Closed Stopped Closing Stopping Req-Sent Ack-Rcvd Ack-Sent Opened */ /* Up */ { {0,0,0,0,CLOSED}, {2,IRC,SCR,0,REQSENT}, {1,ILLEGAL,0,0,CLOSED}, {1,ILLEGAL,0,0,STOPPED}, {1,ILLEGAL,0,0,CLOSING}, {1,ILLEGAL,0,0,STOPPING}, {1,ILLEGAL,0,0,REQSENT}, {1,ILLEGAL,0,0,ACKRCVD}, {1,ILLEGAL,0,0,ACKSENT}, {1,ILLEGAL,0,0,OPENED} }, /* Down */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {0,0,0,0,INITIAL}, {1,TLS,0,0,STARTING}, {0,0,0,0,INITIAL}, {0,0,0,0,STARTING}, {0,0,0,0,STARTING}, {0,0,0,0,STARTING}, {0,0,0,0,STARTING}, {1,TLD,0,0,STARTING} }, /* Open */ { {1,TLS,0,0,STARTING}, {0,0,0,0,STARTING}, {2,IRC,SCR,0,REQSENT}, {0,0,0,0,STOPPED}, {0,0,0,0,STOPPING}, {0,0,0,0,STOPPING}, {0,0,0,0,REQSENT}, {0,0,0,0,ACKRCVD}, {0,0,0,0,ACKSENT}, {0,0,0,0,OPENED} }, /* Close */ { {0,0,0,0,INITIAL}, {1,TLF,0,0,INITIAL}, {0,0,0,0,CLOSED}, {0,0,0,0,CLOSED}, {0,0,0,0,CLOSING}, {0,0,0,0,CLOSING}, {2,IRC,STR,0,CLOSING}, {2,IRC,STR,0,CLOSING}, {2,IRC,STR,0,CLOSING}, {3,TLD,IRC,STR,CLOSING} }, /* TO+ */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,ILLEGAL,0,0,CLOSED}, {1,ILLEGAL,0,0,STOPPED}, {1,STR,0,0,CLOSING}, {1,STR,0,0,STOPPING}, {1,SCR,0,0,REQSENT}, {1,SCR,0,0,REQSENT}, {1,SCR,0,0,ACKSENT}, {1,ILLEGAL,0,0,OPENED} }, /* TO- */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,ILLEGAL,0,0,CLOSED}, {1,ILLEGAL,0,0,STOPPED}, {1,TLF,0,0,CLOSED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {1,ILLEGAL,0,0,OPENED} }, /* RCR+ */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,STA,0,0,CLOSED}, {3,IRC,SCR,SCA,ACKSENT}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {1,SCA,0,0,ACKSENT}, {2,SCA,TLU,0,OPENED}, {1,SCA,0,0,ACKSENT}, {3,TLD,SCR,SCA,ACKSENT} }, /* RCR- */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,STA,0,0,CLOSED}, {3,IRC,SCR,SCN,REQSENT}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {1,SCN,0,0,REQSENT}, {1,SCN,0,0,ACKRCVD}, {1,SCN,0,0,REQSENT}, {3,TLD,SCR,SCN,REQSENT} }, /* RCA */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,STA,0,0,CLOSED}, {1,STA,0,0,STOPPED}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {1,IRC,0,0,ACKRCVD}, {1,SCR,0,0,REQSENT}, {2,IRC,TLU,0,OPENED}, {2,TLD,SCR,0,REQSENT} }, /* RCN */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,STA,0,0,CLOSED}, {1,STA,0,0,STOPPED}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {2,IRC,SCR,0,REQSENT}, {1,SCR,0,0,REQSENT}, {2,IRC,SCR,0,ACKSENT}, {2,TLD,SCR,0,REQSENT} }, /* RTR */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,STA,0,0,CLOSED}, {1,STA,0,0,STOPPED}, {1,STA,0,0,CLOSING}, {1,STA,0,0,STOPPING}, {1,STA,0,0,REQSENT}, {1,STA,0,0,REQSENT}, {1,STA,0,0,REQSENT}, {3,TLD,ZRC,STA,STOPPING} }, /* RTA */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {0,0,0,0,CLOSED}, {0,0,0,0,STOPPED}, {1,TLF,0,0,CLOSED}, {1,TLF,0,0,STOPPED}, {0,0,0,0,REQSENT}, {0,0,0,0,REQSENT}, {0,0,0,0,ACKSENT}, {2,TLD,SCR,0,REQSENT} }, /* RUC */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,SCJ,0,0,CLOSED}, {1,SCJ,0,0,STOPPED}, {1,SCJ,0,0,CLOSING}, {1,SCJ,0,0,STOPPING}, {1,SCJ,0,0,REQSENT}, {1,SCJ,0,0,ACKRCVD}, {1,SCJ,0,0,ACKSENT}, {1,SCJ,0,0,OPENED} }, /* RXJ+ */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {0,0,0,0,CLOSED}, {0,0,0,0,STOPPED}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {0,0,0,0,REQSENT}, {0,0,0,0,REQSENT}, {0,0,0,0,ACKSENT}, {0,0,0,0,OPENED} }, /* RXJ- */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {1,TLF,0,0,CLOSED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,CLOSED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {1,TLF,0,0,STOPPED}, {3,TLD,IRC,STR,STOPPING} }, /* RXR */ { {1,ILLEGAL,0,0,INITIAL},{1,ILLEGAL,0,0,STARTING}, {0,0,0,0,CLOSED}, {0,0,0,0,STOPPED}, {0,0,0,0,CLOSING}, {0,0,0,0,STOPPING}, {0,0,0,0,REQSENT}, {0,0,0,0,ACKRCVD}, {0,0,0,0,ACKSENT}, {1,SER,0,0,OPENED} } }; public final static String[] stateDescriptions = { "In the Initial state, the physical layer is unavailable and no Open has occurred.", "In the Starting state, an administrative Open has been initiated, but the physical layer is unavailable", "In the Closed state, the physical layer is available, but no Open has occurred.", "The Stopped state is a junction state for link termination, link configuration " + "failure, or other failures. In this state, an administrative Close has not occurred.", "In the Closing state, an administrative Close has been initiated, and an attempt is being " + "made to terminate the connection. A Terminate-Request " + "has been sent, but a Terminate-Ack has not yet been received.", "In the Stopping state, an attempt is being made to terminate the link even though " + "an administrative Close has not occurred. This might be due to the reception of " + "a Terminate-Request from the peer, or a catastrophic event, such as the " + "rejection of a Configure-Request packet.", "In the Request-Sent state, an attempt is made to configure the connection. " + "A Configure-Request has been sent but a Configure-Ack has not yet been received.", "In the Ack-Received state, a Configure-Request has been sent and a " + "Configure-Ack has been received, but a Configure-Ack has not yet been sent. " + "This may be because an acceptable Configure-Request has not yet been received from the peer.", "In the Ack-Sent state, a Configure-Request and a Configure-Ack have both been sent, " + "but a Configure-Ack has not yet been received.", "In the Opened state, a Configure-Ack has been both sent and received. The " + "implementation may now signal the upper layers that it is up." }; public final static String[] nextStateDescriptions = { "The automaton has returned to the Initial state.", "The automaton has entered the the Starting state.", "The automaton has entered the Closed state.", "The automaton has entered the Stopped state.", "An attempt is being made to terminate the connection, and the automaton has entered the Closing state.", "An attempt is being made to terminate the link, even though an administrative Close has not occurred. The automaton has entered the Stopping state.", "","","", "The automaton has entered the Opened state." }; } /* ================================================================================================================ Class: Message Represents a message that can travel on the cable. ================================================================================================================ */ class Message { public Message(int x, int v) { this.x = x; this.v = v; } public Message(int x, int v, PPPPacket p, FontMetrics fm) { packet = p; this.x = x; this.v = v; if(p.getInformationField() instanceof LCPPacket) { s = LCPPacket.codeNames[ ( (LCPPacket)(p.getInformationField()) ).getCode() ]; stringHeight = fm.getAscent(); stringWidth = fm.stringWidth(s); bgcolor = Color.white; fgcolor = Color.red; } if(p.getInformationField() instanceof PAPPacket) { s = PAPPacket.codeNames[ ( (PAPPacket)(p.getInformationField()) ).getCode() ]; stringHeight = fm.getAscent(); stringWidth = fm.stringWidth(s); fgcolor = Color.white; bgcolor = Color.blue; } } public PPPPacket packet; // the packet public int x; // position on wire (for animation purposes) // (x<0 or x>cableLength can be used to cause a delay) public int f; // frame number public int v; // velocity public String s; // string for display public int stringHeight = 0; public int stringWidth = 0; public Color bgcolor; public Color fgcolor; public final static int noFrames = 4; } /* ================================================================================================================ Class: PAPProtocol Class for simulating Password Authentication Protocol. ================================================================================================================ */ class PAPProtocol { public PAPProtocol(PPPProtocol p) { parent = p; authPeerWithUs = false; authUsWithPeer = false; } public void peer(boolean b) { authPeerWithUs = b; if(authUsWithPeer && authUsWithPeer) parent.setPhase(parent.NETWORK); } public void us(boolean b) { authUsWithPeer = b; if(authUsWithPeer && authUsWithPeer) parent.setPhase(parent.NETWORK); } public void sendRequest() { PAPPacket o = new PAPPacket(PAPPacket.AUTHENTICATEREQUEST, parent.currentIdentifier); o.addData(new Integer(peerID.length())); o.addData(peerID); o.addData(new Integer(peerPass.length())); o.addData(peerPass); parent.parent.send(parent, new PPPPacket(PPPPacket.PASSWORDAUTHENTICATIONPROTOCOL, o), parent.offset++); } public void receive(PAPPacket p) { parent.offset = 0; if(parent.getState() != PPPProtocol.OPENED) return; parent.displayString.setLength(0); if(p.getCode() == PAPPacket.AUTHENTICATEREQUEST) { parent.displayString.append("An Authenticate-Request has been received."); String rid = (String)p.dataAt(1); String rpass = (String)p.dataAt(3); if(rid.equals(localID) && rpass.equals(localPass)) { // send Ack parent.displayString.append(" The userID and password provided by the peer were acknowledged, so an Authenticate-Ack has been sent."); parent.parent.send(parent, new PPPPacket(PPPPacket.PASSWORDAUTHENTICATIONPROTOCOL, new PAPPacket(PAPPacket.AUTHENTICATEACK, parent.currentIdentifier)), parent.offset++); peer(true); } else { String s; if(rid.equals(localID)) { parent.displayString.append(" However, it contained a bad password."); s = new String("Bad password."); } else { parent.displayString.append(" However, it contained a bad userID."); s = new String("Bad userID."); } // send Nak, decrement counter if(--failureCounter == 0) { // too many bad requests, disconnect peer parent.event(PPPProtocol.CLOSE); parent.displayString.setLength(0); parent.displayString.append(" There have been too many authentication failures. An attempt will be made to terminate the link. A Terminate-Request has been sent."); } else { parent.displayString.append(" Only " + failureCounter + " more authentication failures will be allowed. An Authenticate-Nak has been sent."); PAPPacket nak = new PAPPacket(PAPPacket.AUTHENTICATENAK, parent.currentIdentifier); nak.addData(s); parent.parent.send(parent, new PPPPacket(PPPPacket.PASSWORDAUTHENTICATIONPROTOCOL, nak), parent.offset++); } } } if(p.getCode() == PAPPacket.AUTHENTICATENAK) { parent.displayString.append("An Authenticate-Nak has been received, indicating that the peer did not acknowledge our authentication request. It returned the following message: "); parent.displayString.append("\"" + (String)p.dataAt(0) + "\""); } if(p.getCode() == PAPPacket.AUTHENTICATEACK) { parent.displayString.append("An Authenticate-Ack has been received, indicating that the peer has acknowledged the userID and password."); us(true); } if(authPeerWithUs && authUsWithPeer) { parent.displayString.append(" The link is now availible for network-level traffic."); } } public int getTimer() { return retryTimer; } public int getCounter() { return retryCounter; } public void startRetryTimer() { retryTimer = timeoutValue; } public boolean incrementRestartTimer() { if(retryTimer > -1) { if( (--retryTimer) == 0) { if(--retryCounter == 0) { // terminate link parent.event(PPPProtocol.CLOSE); parent.displayString.setLength(0); parent.displayString.append("The Retry Timer expired, and the Retry Counter no longer continues to be greater than zero. An attempt will be made to terminate the link."); return true; } // resend request parent.displayString.setLength(0); parent.displayString.append("The Retry Timer expired, but the Retry Counter continues to be greater than zero. The Authenticate-Request has been retransmitted."); retryTimer = timeoutValue; parent.offset = 0; sendRequest(); return true; } } if( (retryTimer % 10) == 9) return true; return false; } public void init() { authUsWithPeer = false; authPeerWithUs = false; retryCounter = maxFailure; retryTimer = -1; } boolean authUsWithPeer = false; boolean authPeerWithUs = false; // for configure requests int retryTimer = 1; int retryCounter = -1; int timeoutValue = 259; // for configure naks int maxFailure = 3; int failureCounter = maxFailure; String peerID = "user"; String peerPass = "pass"; String localID = "user"; String localPass = "pass"; PPPProtocol parent; } /* ================================================================================================================ Class: PAPPacket Class for representing Password Authentication Protocol packets. ================================================================================================================ */ class PAPPacket { // ************************************************************ Methods public PAPPacket(int c, int i) { code = c; identifier = i; } public int getCode() { return code; } public int getIdentifier() { return identifier; } public void addData(Object d) { data.addElement(d); } public Object dataAt(int i) throws ArrayIndexOutOfBoundsException { return data.elementAt(i); } public int length() { return data.size(); } public String toString() { return new String(codeNames[code]); } // ************************************************************ Instance Variables // packet fields private int code; // identifies type of LCP packet private int identifier; // aids in matching requests and replies private Vector data = new Vector(); // data field; dependent on code type // ************************************************************ Class Constants // code values public final static int AUTHENTICATEREQUEST = 1; public final static int AUTHENTICATEACK = 2; public final static int AUTHENTICATENAK = 3; // code display names public final static String[] codeNames = { "ILLEGAL CODE", "Authenticate-Request", "Authenticate-Ack", "Authenticate-Nak" }; }