Support RADIUS server outside of the ONOS network

Change-Id: I7e64faae6831467e084db878e02023d40fb33f07
diff --git a/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java b/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java
new file mode 100644
index 0000000..fb513ce
--- /dev/null
+++ b/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.aaa;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.Ethernet;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Set of tests of the ONOS application component. These use an existing RADIUS
+ * server and sends live packets over the network to it.
+ */
+@Ignore ("This should not be run as part of the standard build")
+public class AAAIntegrationTest extends AAATestBase {
+
+    private AAA aaa;
+
+    /**
+     * Mocks the network config registry.
+     */
+    @SuppressWarnings("unchecked")
+    static final class TestNetworkConfigRegistry
+            extends NetworkConfigRegistryAdapter {
+        @Override
+        public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+            return (C) new AAAConfig();
+        }
+    }
+
+    /**
+     * Sets up the services required by the AAA application.
+     */
+    @Before
+    public void setUp() {
+        aaa = new AAA();
+        aaa.netCfgService = new TestNetworkConfigRegistry();
+        aaa.coreService = new CoreServiceAdapter();
+        aaa.packetService = new MockPacketService();
+        aaa.activate();
+    }
+
+    /**
+     * Fetches the sent packet at the given index. The requested packet
+     * must be the last packet on the list.
+     *
+     * @param index index into sent packets array
+     * @return packet
+     */
+    private Ethernet fetchPacket(int index) {
+        for (int iteration = 0; iteration < 20; iteration++) {
+            if (savedPackets.size() > index) {
+                return (Ethernet) savedPackets.get(index);
+            } else {
+                try {
+                    Thread.sleep(250);
+                } catch (Exception ex) {
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Tests the authentication path through the AAA application by sending
+     * packets to the RADIUS server and checking the state machine
+     * transitions.
+     *
+     * @throws Exception when an unhandled error occurs
+     */
+    @Test
+    public void testAuthentication()  throws Exception {
+
+        //  (1) Supplicant start up
+
+        Ethernet startPacket = constructSupplicantStartPacket();
+        sendPacket(startPacket);
+
+        Ethernet responsePacket = fetchPacket(0);
+        assertThat(responsePacket, notNullValue());
+        checkRadiusPacket(aaa, responsePacket, EAP.REQUEST);
+
+        //  (2) Supplicant identify
+
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
+        sendPacket(identifyPacket);
+
+        //  State machine should have been created by now
+
+        StateMachine stateMachine =
+                StateMachine.lookupStateMachineBySessionId(SESSION_ID);
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+        // (3) RADIUS MD5 challenge
+
+        Ethernet radiusChallengeMD5Packet = fetchPacket(1);
+        assertThat(radiusChallengeMD5Packet, notNullValue());
+        checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.REQUEST);
+
+
+        // (4) Supplicant MD5 response
+
+        Ethernet md5RadiusPacket =
+                constructSupplicantIdentifyPacket(stateMachine,
+                                                  EAP.ATTR_MD5,
+                                                  stateMachine.challengeIdentifier(),
+                                                  radiusChallengeMD5Packet);
+        sendPacket(md5RadiusPacket);
+
+
+        // (5) RADIUS Success
+
+        Ethernet successRadiusPacket = fetchPacket(2);
+        assertThat(successRadiusPacket, notNullValue());
+        EAPOL successEAPOL = (EAPOL) successRadiusPacket.getPayload();
+        EAP successEAP = (EAP) successEAPOL.getPayload();
+        assertThat(successEAP.getCode(), is(EAP.SUCCESS));
+
+        //  State machine should be in authorized state
+
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
+
+    }
+
+}
+
diff --git a/src/test/java/org/onosproject/aaa/AAATest.java b/src/test/java/org/onosproject/aaa/AAATest.java
index 75a6033..860a7db 100644
--- a/src/test/java/org/onosproject/aaa/AAATest.java
+++ b/src/test/java/org/onosproject/aaa/AAATest.java
@@ -15,164 +15,61 @@
  */
 package org.onosproject.aaa;
 
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.packet.Data;
+import org.onlab.packet.BasePacket;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EAP;
-import org.onlab.packet.EAPOL;
-import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
-import org.onlab.packet.UDP;
-import org.onlab.packet.VlanId;
 import org.onosproject.core.CoreServiceAdapter;
-import org.onosproject.net.Annotations;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.net.host.HostServiceAdapter;
-import org.onosproject.net.packet.DefaultInboundPacket;
-import org.onosproject.net.packet.DefaultPacketContext;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketServiceAdapter;
-import org.onosproject.net.provider.ProviderId;
 
 import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableSet;
 
-import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.onosproject.net.NetTestTools.connectPoint;
 
 /**
  * Set of tests of the ONOS application component.
  */
-public class AAATest {
+public class AAATest extends AAATestBase {
 
-    MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
-    MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+    static final String BAD_IP_ADDRESS = "198.51.100.0";
 
-    PacketProcessor packetProcessor;
     private AAA aaa;
-    List<Ethernet> savedPackets = new LinkedList<>();
 
-    /**
-     * Saves the given packet onto the saved packets list.
-     *
-     * @param eth packet to save
-     */
-    private void savePacket(Ethernet eth) {
-        savedPackets.add(eth);
+    class AAAWithoutRadiusServer extends AAA {
+        protected void sendRADIUSPacket(RADIUS radiusPacket) {
+            savePacket(radiusPacket);
+        }
     }
 
     /**
-     * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+     * Mocks the AAAConfig class to force usage of an unroutable address for the
+     * RADIUS server.
      */
-    private class MockPacketService extends PacketServiceAdapter {
-
+    static class MockAAAConfig extends AAAConfig {
         @Override
-        public void addProcessor(PacketProcessor processor, int priority) {
-            packetProcessor = processor;
-        }
-
-        @Override
-        public void emit(OutboundPacket packet) {
+        public InetAddress radiusIp() {
             try {
-                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
-                                                                   0, packet.data().array().length);
-                savePacket(eth);
-            } catch (Exception e) {
-                fail(e.getMessage());
+                return InetAddress.getByName(BAD_IP_ADDRESS);
+            } catch (UnknownHostException ex) {
+                // can't happen
+                throw new IllegalStateException(ex);
             }
         }
     }
 
     /**
-     * Mocks the DefaultPacketContext.
-     */
-    private final class TestPacketContext extends DefaultPacketContext {
-
-        private TestPacketContext(long time, InboundPacket inPkt,
-                                  OutboundPacket outPkt, boolean block) {
-            super(time, inPkt, outPkt, block);
-        }
-
-        @Override
-        public void send() {
-            // We don't send anything out.
-        }
-    }
-
-    /**
-     * Mocks a host to allow locating the Radius server.
-     */
-    private static final class MockHost implements Host {
-        @Override
-        public HostId id() {
-            return null;
-        }
-
-        @Override
-        public MacAddress mac() {
-            return null;
-        }
-
-        @Override
-        public VlanId vlan() {
-            return VlanId.vlanId(VlanId.UNTAGGED);
-        }
-
-        @Override
-        public Set<IpAddress> ipAddresses() {
-            return null;
-        }
-
-        @Override
-        public HostLocation location() {
-            return null;
-        }
-
-        @Override
-        public Annotations annotations() {
-            return null;
-        }
-
-        @Override
-        public ProviderId providerId() {
-            return null;
-        }
-    }
-
-    /**
-     * Mocks the Host service.
-     */
-    private static final class MockHostService extends HostServiceAdapter {
-        @Override
-        public Set<Host> getHostsByIp(IpAddress ip) {
-            return ImmutableSet.of(new MockHost());
-        }
-    }
-
-    /**
      * Mocks the network config registry.
      */
     @SuppressWarnings("unchecked")
@@ -180,83 +77,12 @@
             extends NetworkConfigRegistryAdapter {
         @Override
         public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
-            return (C) new AAAConfig();
+            AAAConfig aaaConfig = new MockAAAConfig();
+            return (C) aaaConfig;
         }
     }
 
     /**
-     * Sends an Ethernet packet to the process method of the Packet Processor.
-     *
-     * @param reply Ethernet packet
-     */
-    private void sendPacket(Ethernet reply) {
-        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
-        InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
-                                                          reply,
-                                                          byteBuffer);
-
-        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
-        packetProcessor.process(context);
-    }
-
-    /**
-     * Constructs an Ethernet packet containing a EAPOL_START Payload.
-     *
-     * @return Ethernet packet
-     */
-    private Ethernet constructSupplicantStartPacket() {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
-        eth.setVlanID((short) 2);
-
-        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null);
-
-        //eapol header
-        EAPOL eapol = new EAPOL();
-        eapol.setEapolType(EAPOL.EAPOL_START);
-        eapol.setPacketLength(eap.getLength());
-
-        //eap part
-        eapol.setPayload(eap);
-
-        eth.setPayload(eapol);
-        eth.setPad(true);
-        return eth;
-    }
-
-    /**
-     * Constructs an Ethernet packet containing identification payload.
-     *
-     * @return Ethernet packet
-     */
-    private Ethernet constructSupplicantIdentifyPacket(byte type) {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
-        eth.setVlanID((short) 2);
-
-        String username = "user";
-        EAP eap = new EAP(EAP.REQUEST, (byte) 1, type,
-                          username.getBytes(Charsets.US_ASCII));
-        eap.setIdentifier((byte) 1);
-
-        // eapol header
-        EAPOL eapol = new EAPOL();
-        eapol.setEapolType(EAPOL.EAPOL_PACKET);
-        eapol.setPacketLength(eap.getLength());
-
-        // eap part
-        eapol.setPayload(eap);
-
-        eth.setPayload(eapol);
-        eth.setPad(true);
-        return eth;
-    }
-
-    /**
      * Constructs an Ethernet packet containing a RADIUS challenge
      * packet.
      *
@@ -264,18 +90,9 @@
      * @param challengeType type to use in challenge packet
      * @return Ethernet packet
      */
-    private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
-        eth.setVlanID((short) 2);
+    private RADIUS constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
 
-        IPv4 ipv4 = new IPv4();
-        ipv4.setProtocol(IPv4.PROTOCOL_UDP);
-        ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress());
-
-        String challenge = "1234";
+        String challenge = "12345678901234567";
 
         EAP eap = new EAP(challengeType, (byte) 1, challengeType,
                           challenge.getBytes(Charsets.US_ASCII));
@@ -291,13 +108,7 @@
         radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
                             eap.serialize());
 
-        UDP udp = new UDP();
-        udp.setPayload(radius);
-        ipv4.setPayload(udp);
-
-        eth.setPayload(ipv4);
-        eth.setPad(true);
-        return eth;
+        return radius;
     }
 
     /**
@@ -305,11 +116,10 @@
      */
     @Before
     public void setUp() {
-        aaa = new AAA();
+        aaa = new AAAWithoutRadiusServer();
         aaa.netCfgService = new TestNetworkConfigRegistry();
         aaa.coreService = new CoreServiceAdapter();
         aaa.packetService = new MockPacketService();
-        aaa.hostService = new MockHostService();
         aaa.activate();
     }
 
@@ -324,60 +134,16 @@
     /**
      * Extracts the RADIUS packet from a packet sent by the supplicant.
      *
-     * @param supplicantPacket packet sent by the supplicant
-     * @return RADIUS packet
+     * @param radius RADIUS packet sent by the supplicant
      * @throws DeserializationException if deserialization of the packet contents
      *         fails.
      */
-    private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket)
+    private void checkRADIUSPacketFromSupplicant(RADIUS radius)
             throws DeserializationException {
-        assertThat(supplicantPacket, notNullValue());
-        assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED));
-        assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress));
-        assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress));
-
-        assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class));
-        IPv4 ipv4 = (IPv4) supplicantPacket.getPayload();
-        assertThat(ipv4, notNullValue());
-        assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(),
-                   is(aaa.nasIpAddress.getHostAddress()));
-        assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(),
-                   is(aaa.radiusIpAddress.getHostAddress()));
-
-        assertThat(ipv4.getPayload(), instanceOf(UDP.class));
-        UDP udp = (UDP) ipv4.getPayload();
-        assertThat(udp, notNullValue());
-
-        assertThat(udp.getPayload(), instanceOf(Data.class));
-        Data data = (Data) udp.getPayload();
-        RADIUS radius = RADIUS.deserializer()
-                .deserialize(data.getData(), 0, data.getData().length);
         assertThat(radius, notNullValue());
-        return radius;
-    }
 
-    /**
-     * Checks the contents of a RADIUS packet being sent to the RADIUS server.
-     *
-     * @param radiusPacket packet to check
-     * @param code expected code
-     */
-    private void checkRadiusPacket(Ethernet radiusPacket, byte code) {
-        assertThat(radiusPacket.getVlanID(), is((short) 2));
-
-        // TODO: These address values seem wrong, but are produced by the current AAA implementation
-        assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L)));
-        assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
-
-        assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
-        EAPOL eapol = (EAPOL) radiusPacket.getPayload();
-        assertThat(eapol, notNullValue());
-
-        assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
-        assertThat(eapol.getPayload(), instanceOf(EAP.class));
-        EAP eap = (EAP) eapol.getPayload();
+        EAP eap = radius.decapsulateMessage();
         assertThat(eap, notNullValue());
-        assertThat(eap.getCode(), is(code));
     }
 
     /**
@@ -387,11 +153,10 @@
      * @param index index into sent packets array
      * @return packet
      */
-    private Ethernet fetchPacket(int index) {
-        assertThat(savedPackets.size(), is(index + 1));
-        Ethernet eth = savedPackets.get(index);
-        assertThat(eth, notNullValue());
-        return eth;
+    private BasePacket fetchPacket(int index) {
+        BasePacket packet = savedPackets.get(index);
+        assertThat(packet, notNullValue());
+        return packet;
     }
 
     /**
@@ -400,61 +165,64 @@
      * @throws DeserializationException if packed deserialization fails.
      */
     @Test
-    public void testAuthentication()  throws DeserializationException {
-
-        // Our session id will be the device ID ("of:1") with the port ("1") concatenated
-        String sessionId = "of:11";
+    public void testAuthentication()  throws Exception {
 
         //  (1) Supplicant start up
 
         Ethernet startPacket = constructSupplicantStartPacket();
         sendPacket(startPacket);
 
-        Ethernet responsePacket = fetchPacket(0);
-        checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY);
+        Ethernet responsePacket = (Ethernet) fetchPacket(0);
+        checkRadiusPacket(aaa, responsePacket, EAP.ATTR_IDENTITY);
 
         //  (2) Supplicant identify
 
-        Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY);
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
         sendPacket(identifyPacket);
 
-        Ethernet radiusIdentifyPacket = fetchPacket(1);
+        RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
 
-        RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket);
-        assertThat(radiusAccessRequest, notNullValue());
-        assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
-        assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
-                   is("user"));
+        checkRADIUSPacketFromSupplicant(radiusIdentifyPacket);
+
+        assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+        assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
+                   is("testuser"));
 
         IpAddress nasIp =
                 IpAddress.valueOf(IpAddress.Version.INET,
-                                  radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
+                                  radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
                                           .getValue());
         assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress()));
 
         //  State machine should have been created by now
 
         StateMachine stateMachine =
-                StateMachine.lookupStateMachineBySessionId(sessionId);
+                StateMachine.lookupStateMachineBySessionId(SESSION_ID);
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
 
         // (3) RADIUS MD5 challenge
 
-        Ethernet radiusCodeAccessChallengePacket =
+        RADIUS radiusCodeAccessChallengePacket =
                 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
-        sendPacket(radiusCodeAccessChallengePacket);
+        aaa.radiusListener.handleRadiusPacket(radiusCodeAccessChallengePacket);
 
-        Ethernet radiusChallengeMD5Packet = fetchPacket(2);
-        checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5);
+        Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
+        checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.ATTR_MD5);
 
         // (4) Supplicant MD5 response
 
-        Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5);
+        Ethernet md5RadiusPacket =
+                constructSupplicantIdentifyPacket(stateMachine,
+                                                  EAP.ATTR_MD5,
+                                                  stateMachine.challengeIdentifier(),
+                                                  radiusChallengeMD5Packet);
         sendPacket(md5RadiusPacket);
-        Ethernet supplicantMD5ResponsePacket = fetchPacket(3);
-        RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket);
-        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1));
+
+        RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
+
+        checkRADIUSPacketFromSupplicant(responseMd5RadiusPacket);
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 0));
         assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
 
         //  State machine should be in pending state
@@ -462,37 +230,20 @@
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
 
-        // (5) RADIUS TLS Challenge
+        // (5) RADIUS Success
 
-        Ethernet radiusCodeAccessChallengeTLSPacket =
-                constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS);
-        sendPacket(radiusCodeAccessChallengeTLSPacket);
-
-        Ethernet radiusChallengeTLSPacket = fetchPacket(4);
-        checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS);
-
-        // (6) Supplicant TLS response
-
-        Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS);
-        sendPacket(tlsRadiusPacket);
-        Ethernet supplicantTLSResponsePacket = fetchPacket(5);
-        RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket);
-        assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0));
-        assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
-
-        // (7) RADIUS Success
-
-        Ethernet successPacket =
+        RADIUS successPacket =
                 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
-        sendPacket(successPacket);
-        Ethernet supplicantSuccessPacket = fetchPacket(6);
+        aaa.radiusListener.handleRadiusPacket((successPacket));
+        Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
 
-        checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS);
+        checkRadiusPacket(aaa, supplicantSuccessPacket, EAP.SUCCESS);
 
         //  State machine should be in authorized state
 
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
+
     }
 
     /**
@@ -502,7 +253,7 @@
     public void testConfig() {
         assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP));
         assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC));
-        assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP));
+        assertThat(aaa.radiusIpAddress.getHostAddress(), is(BAD_IP_ADDRESS));
         assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC));
     }
 }
diff --git a/src/test/java/org/onosproject/aaa/AAATestBase.java b/src/test/java/org/onosproject/aaa/AAATestBase.java
new file mode 100644
index 0000000..dffcba2
--- /dev/null
+++ b/src/test/java/org/onosproject/aaa/AAATestBase.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.aaa;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.onosproject.net.NetTestTools.connectPoint;
+
+/**
+ * Common methods for AAA app testing.
+ */
+public class AAATestBase {
+
+    MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
+    MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+
+    // Our session id will be the device ID ("of:1") with the port ("1") concatenated
+    static final String SESSION_ID = "of:11";
+
+    List<BasePacket> savedPackets = new LinkedList<>();
+    PacketProcessor packetProcessor;
+
+    /**
+     * Saves the given packet onto the saved packets list.
+     *
+     * @param packet packet to save
+     */
+    void savePacket(BasePacket packet) {
+        savedPackets.add(packet);
+    }
+
+    /**
+     * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+     */
+    class MockPacketService extends PacketServiceAdapter {
+
+        @Override
+        public void addProcessor(PacketProcessor processor, int priority) {
+            packetProcessor = processor;
+        }
+
+        @Override
+        public void emit(OutboundPacket packet) {
+            try {
+                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
+                                                                   0, packet.data().array().length);
+                savePacket(eth);
+            } catch (Exception e) {
+                fail(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Mocks the DefaultPacketContext.
+     */
+    final class TestPacketContext extends DefaultPacketContext {
+
+        private TestPacketContext(long time, InboundPacket inPkt,
+                                  OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+            // We don't send anything out.
+        }
+    }
+
+    /**
+     * Sends an Ethernet packet to the process method of the Packet Processor.
+     *
+     * @param reply Ethernet packet
+     */
+    void sendPacket(Ethernet reply) {
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+        InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
+                                                          reply,
+                                                          byteBuffer);
+
+        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
+        packetProcessor.process(context);
+    }
+
+    /**
+     * Constructs an Ethernet packet containing identification payload.
+     *
+     * @return Ethernet packet
+     */
+    Ethernet constructSupplicantIdentifyPacket(StateMachine stateMachine,
+                                                       byte type,
+                                                       byte id,
+                                                       Ethernet radiusChallenge)
+            throws Exception {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        String username = "testuser";
+        byte[] data = username.getBytes();
+
+
+        if (type == EAP.ATTR_MD5) {
+            String password = "testpassword";
+            EAPOL eapol = (EAPOL) radiusChallenge.getPayload();
+            EAP eap = (EAP) eapol.getPayload();
+
+            byte[] identifier = new byte[password.length() + eap.getData().length];
+
+            identifier[0] = stateMachine.challengeIdentifier();
+            System.arraycopy(password.getBytes(), 0, identifier, 1, password.length());
+            System.arraycopy(eap.getData(), 1, identifier, 1 + password.length(), 16);
+
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] hash = md.digest(identifier);
+            data = new byte[17];
+            data[0] = (byte) 16;
+            System.arraycopy(hash, 0, data, 1, 16);
+        }
+        EAP eap = new EAP(EAP.RESPONSE, (byte) 1, type,
+                          data);
+        eap.setIdentifier(id);
+
+        // eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_PACKET);
+        eapol.setPacketLength(eap.getLength());
+
+        // eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Constructs an Ethernet packet containing a EAPOL_START Payload.
+     *
+     * @return Ethernet packet
+     */
+    Ethernet constructSupplicantStartPacket() {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 2, EAPOL.EAPOL_START, null);
+
+        // eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_START);
+        eapol.setPacketLength(eap.getLength());
+
+        // eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Checks the contents of a RADIUS packet being sent to the RADIUS server.
+     *
+     * @param radiusPacket packet to check
+     * @param code expected code
+     */
+    void checkRadiusPacket(AAA aaa, Ethernet radiusPacket, byte code) {
+
+        assertThat(radiusPacket.getSourceMAC(),
+                   is(MacAddress.valueOf(aaa.nasMacAddress)));
+        assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
+
+        assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
+        EAPOL eapol = (EAPOL) radiusPacket.getPayload();
+        assertThat(eapol, notNullValue());
+
+        assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
+        assertThat(eapol.getPayload(), instanceOf(EAP.class));
+        EAP eap = (EAP) eapol.getPayload();
+        assertThat(eap, notNullValue());
+
+        assertThat(eap.getCode(), is(code));
+    }
+}
diff --git a/src/test/java/org/onosproject/aaa/StateMachineTest.java b/src/test/java/org/onosproject/aaa/StateMachineTest.java
index 04837e8..1838c63 100644
--- a/src/test/java/org/onosproject/aaa/StateMachineTest.java
+++ b/src/test/java/org/onosproject/aaa/StateMachineTest.java
@@ -21,7 +21,9 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import static org.junit.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 
 public class StateMachineTest {