VOL-149: Configure RADIUS Attributes as per subscriber information
VOL-148: RADIUS VLAN ID configurable

Change-Id: I2a51dbf316637e685b7e3c36595a27922c79b23c
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..179aaf7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,53 @@
+# ONOS AAA Application
+
+The ONOS AAA application behaves as a NAS Server and does RADIUS authentication of the ports. It maintains state machines for each of the ports from which it receives EAP Start messages to maintain the current status of the authentication procedure.
+
+# Configuration
+```sh
+ "org.opencord.aaa" : {
+      "AAA" : {
+         "nasIp": "192.168.1.251",
+         "nasMac" : "00:1b:22:34:55:78",
+         "radiusSecret": "testing123",
+         "radiusIp": "192.168.1.254",
+         "radiusServerPort" : "1812",
+         "radiusMac" : "00:1e:67:d2:ee:f7",
+         "vlanId" : "4000",
+         "useSocket" : "false",
+         "radiusServerConnectPoints": [ "of:00000000000000b2/2" ],
+         "packetCustomizer" : "sample"
+     }
+ ```
+ ### Configuration Parameters
+##### nasIp
+ IP Address of the NAS  which is requesting authentication of the user
+##### nasMac
+MAC Address of the NAS  which is requesting authentication of the user `(Used only when useSocket is false)`
+##### radiusSecret
+Shared secret
+##### radiusIp
+IP Address of the RADIUS Server
+##### radiusServerPort
+UDP Port Number on which RADIUS Server is listening
+##### radiusMac
+MAC address of the RADIUS server or next hop router `(Used only when useSocket is false)`
+##### vlanId
+VLAN on which the RADIUS Server is available `(Used only when useSocket is false)`
+##### useSocket
+There are two options for communication with the Radius Server
+- Communication using a UDP socket
+- Communication using directly the port of the SDN switch connected to the Radius Server.
+
+When useSocket is false, the RADIUS packets sent out would carry the IP and MAC address of the device from which the EAP packets were received. That device should be available in the `SubscriberAndDeviceInformationService (Sadis)`. AAA application fetches data from Sadis based on the serial number of the device.
+
+##### radiusServerConnectPoints
+Connect point of SDN switch through which the RADIUS Server is reachable `(Used only when useSocket is false)`
+
+##### packetCustomizer
+The values of RADIUS attributes expected by the RADIUS Server might be different in different scenarios or in case of different Operators.
+
+As of today AAA App provides two different customizers
+"default" : When you set the value as this, no customization is done to the RADIUS packets
+"sample" : This is a sample customization wherein specific RADIUS attributes and filled with values from `Sadis` Service. The src MAC and src IP of the RADIUS messages are set according to the OLT device (from which the EAP Start message is received) configured in `Sadis`
+
+More customizers might be added to AAA App later which can fill Subscriber specific atrributes into the RADIUS attributes/messages by querying data from `Sadis`. The key to get data from Sadis is the PortName of the Port from which EAP messages are received.
diff --git a/pom.xml b/pom.xml
index 2210923..6edbf2c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,10 @@
         <onos.app.category>Security</onos.app.category>
         <onos.app.url>http://opencord.org</onos.app.url>
         <onos.app.readme>802.1x authentication service.</onos.app.readme>
+        <onos.app.requires>
+              org.onosproject.olt,
+              org.opencord.sadis
+        </onos.app.requires>
     </properties>
 
     <dependencies>
@@ -50,6 +54,18 @@
         </dependency>
 
         <dependency>
+            <groupId>org.opencord</groupId>
+            <artifactId>sadis-api</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+             <groupId>org.opencord</groupId>
+             <artifactId>olt-api</artifactId>
+             <version>1.2-SNAPSHOT</version>
+         </dependency>
+
+        <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-api</artifactId>
             <version>${onos.version}</version>
@@ -89,4 +105,31 @@
             </plugin>
         </plugins>
     </build>
+
+    <repositories>
+        <repository>
+            <id>central</id>
+            <name>Central Repository</name>
+            <url>http://repo.maven.apache.org/maven2</url>
+            <layout>default</layout>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <releases>
+                <enabled>true</enabled>
+                <updatePolicy>always</updatePolicy>
+                <checksumPolicy>fail</checksumPolicy>
+            </releases>
+        </repository>
+
+        <repository>
+            <id>snapshots</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>always</updatePolicy>
+                <checksumPolicy>fail</checksumPolicy>
+            </snapshots>
+        </repository>
+    </repositories>
 </project>
diff --git a/src/main/java/org/opencord/aaa/AaaConfig.java b/src/main/java/org/opencord/aaa/AaaConfig.java
index 0dfa142..8ffdf00 100644
--- a/src/main/java/org/opencord/aaa/AaaConfig.java
+++ b/src/main/java/org/opencord/aaa/AaaConfig.java
@@ -15,13 +15,22 @@
  */
 package org.opencord.aaa;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+import com.google.common.collect.ImmutableSet;
+
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.basics.BasicElementConfig;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Network config for the AAA app.
  */
@@ -33,8 +42,16 @@
     private static final String NAS_IP = "nasIp";
     private static final String NAS_MAC = "nasMac";
     private static final String RADIUS_SECRET = "radiusSecret";
-    private static final String RADIUS_SWITCH = "radiusSwitch";
-    private static final String RADIUS_PORT = "radiusPort";
+    private static final String RADIUS_VLAN_ID = "vlanId";
+    private static final String RADIUS_VLAN_PRIORITY_BIT = "radiusPBit";
+    private static final String RADIUS_CONNECTION_TYPE =  "radiusConnectionType";
+    private static final String RADIUS_SERVER_CONNECTPOINTS = "radiusServerConnectPoints";
+    // Which packet customizer to use
+    // "packetCustomizer" : "sample" -- Means use SamplePAcketCustomizer
+    // "packetCustomizer" : "default" -- No customization of packets
+    // if param is missing it is treated as default
+    // This class should be a subclass of PacketCustomizer
+    private static final String PACKET_CUSTOMIZER = "packetCustomizer";
 
     // RADIUS server IP address
     protected static final String DEFAULT_RADIUS_IP = "10.128.10.4";
@@ -51,15 +68,22 @@
     // RADIUS server shared secret
     protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
 
-    // Radius Switch Id
-    protected static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
-
-    // Radius Port Number
-    protected static final String DEFAULT_RADIUS_PORT = "1811";
-
     // Radius Server UDP Port Number
     protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812";
 
+    // Radius Server Vlan ID
+    protected static final String DEFAULT_RADIUS_VLAN_ID = "4093";
+
+    // Radius Sever P-Bit
+    protected static final String DEFAULT_RADIUS_VLAN_PRIORITY_BIT = "3";
+
+    // Whether to use socket or not to communicate with RADIUS Server
+    protected static final String DEFAULT_RADIUS_CONNECTION_TYPE = "socket";
+
+    // Packet Customizer Default value
+    protected static final String DEFAULT_PACKET_CUSTOMIZER = "default";
+
+
     /**
      * Gets the value of a string property, protecting for an empty
      * JSON object.
@@ -179,44 +203,6 @@
     }
 
     /**
-     * Returns the ID of the RADIUS switch.
-     *
-     * @return radius switch ID or null if not set
-     */
-    public String radiusSwitch() {
-        return getStringProperty(RADIUS_SWITCH, DEFAULT_RADIUS_SWITCH);
-    }
-
-    /**
-     * Sets the ID of the RADIUS switch.
-     *
-     * @param switchId new RADIUS switch ID; null to clear
-     * @return self
-     */
-    public BasicElementConfig radiusSwitch(String switchId) {
-        return (BasicElementConfig) setOrClear(RADIUS_SWITCH, switchId);
-    }
-
-    /**
-     * Returns the RADIUS port.
-     *
-     * @return radius port or null if not set
-     */
-    public long radiusPort() {
-        return Integer.parseInt(getStringProperty(RADIUS_PORT, DEFAULT_RADIUS_PORT));
-    }
-
-    /**
-     * Sets the RADIUS port.
-     *
-     * @param port new RADIUS port; null to clear
-     * @return self
-     */
-    public BasicElementConfig radiusPort(long port) {
-        return (BasicElementConfig) setOrClear(RADIUS_PORT, port);
-    }
-
-    /**
      * Returns the RADIUS server UDP port.
      *
      * @return radius server UDP port.
@@ -236,4 +222,69 @@
         return (BasicElementConfig) setOrClear(RADIUS_SERVER_PORT, (long) port);
     }
 
+    /**
+     * Returns the RADIUS server vlan ID.
+     *
+     * @return Radius Server VLan id or default if not set
+     */
+    public short radiusServerVlanId() {
+        return Short.parseShort(getStringProperty(RADIUS_VLAN_ID, DEFAULT_RADIUS_VLAN_ID));
+    }
+
+    /**
+     * Returns the type of connection to use to communicate with the RADIUS Server.
+     *
+     * @return "socket" or "packet_out"
+     */
+    public String radiusConnectionType() {
+        return getStringProperty(RADIUS_CONNECTION_TYPE, DEFAULT_RADIUS_CONNECTION_TYPE);
+    }
+
+    /**
+     * Returns the RADIUS server p-bit.
+     *
+     * @return Radius Server P-bit to use, default if not set
+     */
+    public byte radiusServerPBit() {
+        return Byte.parseByte(getStringProperty(RADIUS_VLAN_PRIORITY_BIT, DEFAULT_RADIUS_VLAN_PRIORITY_BIT));
+    }
+
+    /**
+     * Returns the PACKET CUSTOMIZER CLASS NAME.
+     *
+     * @return PACKET CUSTOMIZER, default if not set
+     */
+    public String radiusPktCustomizer() {
+        return getStringProperty(PACKET_CUSTOMIZER, DEFAULT_PACKET_CUSTOMIZER);
+    }
+
+    /**
+     * Returns the List of ConnectPoints to reach the Radius Server.
+     *
+     * @return List of ConnectPoints
+     */
+    public Set<ConnectPoint> radiusServerConnectPoints() {
+        if (object == null) {
+            return new HashSet<ConnectPoint>();
+        }
+
+        if (!object.has(RADIUS_SERVER_CONNECTPOINTS)) {
+            return ImmutableSet.of();
+        }
+
+        ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+        ArrayNode arrayNode = (ArrayNode) object.path(RADIUS_SERVER_CONNECTPOINTS);
+        for (JsonNode jsonNode : arrayNode) {
+            String portName = jsonNode.asText(null);
+            if (portName == null) {
+                return null;
+            }
+            try {
+                builder.add(ConnectPoint.deviceConnectPoint(portName));
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+        }
+        return builder.build();
+    }
 }
diff --git a/src/main/java/org/opencord/aaa/AaaManager.java b/src/main/java/org/opencord/aaa/AaaManager.java
index f945ebc..2f885db 100755
--- a/src/main/java/org/opencord/aaa/AaaManager.java
+++ b/src/main/java/org/opencord/aaa/AaaManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 AT&T Foundry
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,21 +15,12 @@
  */
 package org.opencord.aaa;
 
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.packet.DeserializationException;
+
 import org.onlab.packet.EAP;
 import org.onlab.packet.EAPOL;
 import org.onlab.packet.EthType;
@@ -37,8 +28,12 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
+import org.onlab.packet.VlanId;
+
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -46,9 +41,8 @@
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -56,14 +50,20 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
+
+import org.opencord.olt.AccessDeviceService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.SubscriberAndDeviceInformationService;
+
 import org.slf4j.Logger;
 
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-import static org.onosproject.net.packet.PacketPriority.CONTROL;
+
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
 /**
  * AAA application for ONOS.
  */
@@ -86,25 +86,38 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry netCfgService;
 
-    // Parsed RADIUS server addresses
-    protected InetAddress radiusIpAddress;
-    protected String radiusMacAddress;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected SubscriberAndDeviceInformationService subsService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected AccessDeviceService accessDeviceService;
 
     // NAS IP address
     protected InetAddress nasIpAddress;
-    protected String nasMacAddress;
+
+    // self MAC address
+    protected static String nasMacAddress;
+
+    // Parsed RADIUS server addresses
+    protected InetAddress radiusIpAddress;
+
+    // MAC address of RADIUS server or net hop router
+    protected String radiusMacAddress;
 
     // RADIUS server secret
     protected String radiusSecret;
 
-    // ID of RADIUS switch
-    protected String radiusSwitch;
+    // NAS Identifier
+    protected String nasId;
 
-    // RADIUS port number
-    protected long radiusPort;
-
-    // RADIUS server TCP port number
-    protected short radiusServerPort;
+    // bindings
+    protected CustomizationInfo customInfo;
 
     // our application-specific event handler
     private ReactivePacketProcessor processor = new ReactivePacketProcessor();
@@ -112,11 +125,22 @@
     // our unique identifier
     private ApplicationId appId;
 
-    // Socket used for UDP communications with RADIUS server
-    private DatagramSocket radiusSocket;
+    // Setup specific customization/attributes on the RADIUS packets
+    PacketCustomizer pktCustomizer;
 
-    // Executor for RADIUS communication thread
-    private ExecutorService executor;
+    // packet customizer to use
+    private String customizer;
+
+    // Type of connection to use to communicate with Radius server, options are
+    // "socket" or "packet_out"
+    private String radiusConnectionType;
+
+    // Object for the spcific type of communication with the RADIUS
+    // server, socket based or packet_out based
+    RadiusCommunicator impl = null;
+
+    // latest configuration
+    AaaConfig newCfg;
 
     // Configuration properties factory
     private final ConfigFactory factory =
@@ -143,7 +167,7 @@
      * @return Ethernet frame
      */
     private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
-                                               short vlan, byte eapolType, EAP eap) {
+                                               short vlan, byte eapolType, EAP eap, byte priorityCode) {
 
         Ethernet eth = new Ethernet();
         eth.setDestinationMACAddress(dstMac.toBytes());
@@ -151,6 +175,7 @@
         eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
         if (vlan != Ethernet.VLAN_UNTAGGED) {
             eth.setVlanID(vlan);
+            eth.setPriorityCode(priorityCode);
         }
         //eapol header
         EAPOL eapol = new EAPOL();
@@ -165,86 +190,143 @@
         return eth;
     }
 
-    private void initializeLocalState() {
-        try {
-            radiusSocket = new DatagramSocket(null);
-            radiusSocket.setReuseAddress(true);
-            radiusSocket.bind(new InetSocketAddress((int) radiusPort));
-        } catch (Exception ex) {
-            log.error("Can't open RADIUS socket", ex);
-        }
-
-        executor = Executors.newSingleThreadExecutor(
-                new ThreadFactoryBuilder()
-                        .setNameFormat("AAA-radius-%d").build());
-        executor.execute(radiusListener);
-    }
-
     @Activate
     public void activate() {
         netCfgService.registerConfigFactory(factory);
-        // "org.onosproject.aaa" is the FQDN of our app
         appId = coreService.registerApplication(APP_NAME);
 
         cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
 
         // register our event handler
         packetService.addProcessor(processor, PacketProcessor.director(2));
-        requestIntercepts();
+
+        customInfo = new CustomizationInfo(subsService, deviceService);
+
+        switch (customizer.toLowerCase()) {
+            case "sample":
+                pktCustomizer = new SamplePacketCustomizer(customInfo);
+                log.info("Created SamplePacketCustomizer");
+                break;
+            default:
+                pktCustomizer = new PacketCustomizer(customInfo);
+                log.info("Created default PacketCustomizer");
+                break;
+        }
+
+        if (radiusConnectionType.toLowerCase().equals("socket")) {
+            impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
+        } else {
+            impl = new PortBasedRadiusCommunicator(appId, packetService, mastershipService,
+                    deviceService, subsService, pktCustomizer, this);
+        }
 
         StateMachine.initializeMaps();
+        StateMachine.setAccessDeviceService(accessDeviceService);
 
-        initializeLocalState();
+        impl.initializeLocalState(newCfg);
         netCfgService.addListener(cfgListener);
 
+        impl.requestIntercepts();
+
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        withdrawIntercepts();
+        impl.withdrawIntercepts();
         // de-register and null our handler
         packetService.removeProcessor(processor);
         processor = null;
         StateMachine.destroyMaps();
-        radiusSocket.close();
-        executor.shutdownNow();
+
+        impl.deactivate();
+
         log.info("Stopped");
     }
 
-    protected void sendRadiusPacket(RADIUS radiusPacket) {
+    /**
+     * Send RADIUS packet to the RADIUS server.
+     *
+     * @param radiusPacket RADIUS packet to be sent to server.
+     * @param inPkt        Incoming EAPOL packet
+     */
+    protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
+        impl.sendRadiusPacket(radiusPacket, inPkt);
+    }
 
-        try {
-            final byte[] data = radiusPacket.serialize();
-            final DatagramSocket socket = radiusSocket;
-
-            DatagramPacket packet =
-                    new DatagramPacket(data, data.length,
-                                       radiusIpAddress, radiusServerPort);
-
-            socket.send(packet);
-        } catch (IOException e) {
-            log.info("Cannot send packet to RADIUS server", e);
+    /**
+     * Handles RADIUS packets.
+     *
+     * @param radiusPacket RADIUS packet coming from the RADIUS server.
+     * @throws StateMachineException if an illegal state transition is triggered
+     */
+    public void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
+        StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
+        if (stateMachine == null) {
+            log.error("Invalid session identifier {}, exiting...", radiusPacket.getIdentifier());
+            return;
         }
-    }
 
-    /**
-     * Request packet in via PacketService.
-     */
-    private void requestIntercepts() {
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
-        packetService.requestPackets(selector.build(),
-                                     CONTROL, appId);
-    }
+        EAP eapPayload;
+        Ethernet eth;
 
-    /**
-     * Cancel request for packet in via PacketService.
-     */
-    private void withdrawIntercepts() {
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
-        packetService.cancelPackets(selector.build(), CONTROL, appId);
+        switch (radiusPacket.getCode()) {
+            case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
+                RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
+                byte[] challengeState = null;
+                if (radiusAttrState != null) {
+                    challengeState = radiusAttrState.getValue();
+                }
+                eapPayload = radiusPacket.decapsulateMessage();
+                stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
+                eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                        MacAddress.valueOf(nasMacAddress),
+                        stateMachine.vlanId(),
+                        EAPOL.EAPOL_PACKET,
+                        eapPayload, stateMachine.priorityCode());
+                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                break;
+            case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
+                //send an EAPOL - Success to the supplicant.
+                byte[] eapMessageSuccess =
+                        radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
+                eapPayload = new EAP();
+                eapPayload = (EAP) eapPayload.deserialize(eapMessageSuccess, 0, eapMessageSuccess.length);
+                eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                        MacAddress.valueOf(nasMacAddress),
+                        stateMachine.vlanId(),
+                        EAPOL.EAPOL_PACKET,
+                        eapPayload, stateMachine.priorityCode());
+                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+
+                stateMachine.authorizeAccess();
+
+                break;
+            case RADIUS.RADIUS_CODE_ACCESS_REJECT:
+                //send an EAPOL - Failure to the supplicant.
+                byte[] eapMessageFailure;
+                eapPayload = new EAP();
+                RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
+                if (radiusAttrEap == null) {
+                    eapPayload.setCode(EAP.FAILURE);
+                    eapPayload.setIdentifier(stateMachine.challengeIdentifier());
+                    eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
+                } else {
+                    eapMessageFailure = radiusAttrEap.getValue();
+                    eapPayload = (EAP) eapPayload.deserialize(eapMessageFailure, 0, eapMessageFailure.length);
+                }
+                eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                        MacAddress.valueOf(nasMacAddress),
+                        stateMachine.vlanId(),
+                        EAPOL.EAPOL_PACKET,
+                        eapPayload, stateMachine.priorityCode());
+                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                stateMachine.denyAccess();
+
+                break;
+            default:
+                log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
+        }
     }
 
     /**
@@ -275,6 +357,7 @@
             if (ethPkt == null) {
                 return;
             }
+
             try {
                 // identify if incoming packet comes from supplicant (EAP) or RADIUS
                 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
@@ -282,11 +365,11 @@
                         handleSupplicantPacket(context.inPacket());
                         break;
                     default:
-                        log.trace("Skipping Ethernet packet type {}",
-                                  EthType.EtherType.lookup(ethPkt.getEtherType()));
+                        // any other packets let the specific implementation handle
+                        impl.handlePacketFromServer(context);
                 }
             } catch (StateMachineException e) {
-                log.warn("Unable to process RADIUS packet:", e);
+                log.warn("Unable to process packet:", e);
             }
         }
 
@@ -332,9 +415,19 @@
             String sessionId = deviceId.toString() + portNumber.toString();
             StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
             if (stateMachine == null) {
-                stateMachine = new StateMachine(sessionId);
-            }
+                if (deviceService != null) {
+                    String nasPortId = deviceService.getPort(inPacket.receivedFrom()).
+                            annotations().value(AnnotationKeys.PORT_NAME);
 
+                    SubscriberAndDeviceInformation subscriber =
+                            subsService.get(nasPortId);
+                    if (subscriber != null) {
+                        stateMachine = new StateMachine(sessionId, subscriber.cTag());
+                    }
+                } else {
+                    stateMachine = new StateMachine(sessionId, VlanId.vlanId((short) 0));
+                }
+            }
 
             EAPOL eapol = (EAPOL) ethPkt.getPayload();
 
@@ -345,9 +438,12 @@
 
                     //send an EAP Request/Identify to the supplicant
                     EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
+                    if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
+                       stateMachine.setPriorityCode(ethPkt.getPriorityCode());
+                    }
                     Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
                                                       ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
-                                                      eapPayload);
+                                                      eapPayload, stateMachine.priorityCode());
                     stateMachine.setSupplicantAddress(srcMac);
                     stateMachine.setVlanId(ethPkt.getVlanID());
 
@@ -373,9 +469,10 @@
                             stateMachine.setUsername(eapPacket.getData());
 
                             radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
+                            radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
                             radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
 
-                            sendRadiusPacket(radiusPayload);
+                            sendRadiusPacket(radiusPayload, inPacket);
 
                             // change the state to "PENDING"
                             stateMachine.requestAccess();
@@ -390,18 +487,20 @@
                                         getRadiusPayload(stateMachine,
                                                          stateMachine.identifier(),
                                                          eapPacket);
+                                radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
 
                                 if (stateMachine.challengeState() != null) {
                                     radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
                                             stateMachine.challengeState());
                                 }
                                 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
-                                sendRadiusPacket(radiusPayload);
+                                sendRadiusPacket(radiusPayload, inPacket);
                             }
                             break;
                         case EAP.ATTR_TLS:
                             // request id access to RADIUS
                             radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
+                            radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
 
                             if (stateMachine.challengeState() != null) {
                                 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
@@ -410,7 +509,7 @@
                             stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
 
                             radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
-                            sendRadiusPacket(radiusPayload);
+                            sendRadiusPacket(radiusPayload, inPacket);
 
                             if (stateMachine.state() != StateMachine.STATE_PENDING) {
                                 stateMachine.requestAccess();
@@ -424,123 +523,12 @@
                 default:
                     log.trace("Skipping EAPOL message {}", eapol.getEapolType());
             }
-
         }
     }
 
-    class RadiusListener implements Runnable {
-
-        /**
-         * Handles RADIUS packets.
-         *
-         * @param radiusPacket RADIUS packet coming from the RADIUS server.
-         * @throws StateMachineException if an illegal state transition is triggered
-         */
-        protected void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
-            StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
-            if (stateMachine == null) {
-                log.error("Invalid session identifier, exiting...");
-                return;
-            }
-
-            EAP eapPayload;
-            Ethernet eth;
-            switch (radiusPacket.getCode()) {
-                case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
-                    RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
-                    byte[] challengeState = null;
-                    if (radiusAttrState != null) {
-                        challengeState = radiusAttrState.getValue();
-                    }
-                    eapPayload = radiusPacket.decapsulateMessage();
-                    stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
-                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                                             MacAddress.valueOf(nasMacAddress),
-                                             stateMachine.vlanId(),
-                                             EAPOL.EAPOL_PACKET,
-                                             eapPayload);
-                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
-                    break;
-                case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
-                    //send an EAPOL - Success to the supplicant.
-                    byte[] eapMessageSuccess =
-                            radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
-                    eapPayload = new EAP();
-                    eapPayload = (EAP) eapPayload.deserialize(eapMessageSuccess, 0, eapMessageSuccess.length);
-                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                                             MacAddress.valueOf(nasMacAddress),
-                                             stateMachine.vlanId(),
-                                             EAPOL.EAPOL_PACKET,
-                                             eapPayload);
-                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
-
-                    stateMachine.authorizeAccess();
-                    break;
-                case RADIUS.RADIUS_CODE_ACCESS_REJECT:
-                    //send an EAPOL - Failure to the supplicant.
-                    byte[] eapMessageFailure;
-                    eapPayload = new EAP();
-                    RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
-                    if (radiusAttrEap == null) {
-                        eapPayload.setCode(EAP.FAILURE);
-                        eapPayload.setIdentifier(stateMachine.challengeIdentifier());
-                        eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
-                    } else {
-                        eapMessageFailure = radiusAttrEap.getValue();
-                        eapPayload = (EAP) eapPayload.deserialize(eapMessageFailure, 0, eapMessageFailure.length);
-                    }
-                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                                             MacAddress.valueOf(nasMacAddress),
-                                             stateMachine.vlanId(),
-                                             EAPOL.EAPOL_PACKET,
-                                             eapPayload);
-                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
-                    stateMachine.denyAccess();
-                    break;
-                default:
-                    log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
-            }
-        }
-
-
-        @Override
-        public void run() {
-            boolean done = false;
-            int packetNumber = 1;
-
-            log.info("UDP listener thread starting up");
-            RADIUS inboundRadiusPacket;
-            while (!done) {
-                try {
-                    byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH];
-                    DatagramPacket inboundBasePacket =
-                            new DatagramPacket(packetBuffer, packetBuffer.length);
-                    DatagramSocket socket = radiusSocket;
-                    socket.receive(inboundBasePacket);
-                    log.info("Packet #{} received", packetNumber++);
-                    try {
-                        inboundRadiusPacket =
-                                RADIUS.deserializer()
-                                        .deserialize(inboundBasePacket.getData(),
-                                                     0,
-                                                     inboundBasePacket.getLength());
-                        handleRadiusPacket(inboundRadiusPacket);
-                    } catch (DeserializationException dex) {
-                        log.error("Cannot deserialize packet", dex);
-                    } catch (StateMachineException sme) {
-                        log.error("Illegal state machine operation", sme);
-                    }
-
-                } catch (IOException e) {
-                    log.info("Socket was closed, exiting listener thread");
-                    done = true;
-                }
-            }
-        }
-    }
-
-    RadiusListener radiusListener = new RadiusListener();
-
+    /**
+     * Configuration Listener, handles change in configuration.
+     */
     private class InternalConfigListener implements NetworkConfigListener {
 
         /**
@@ -550,7 +538,7 @@
          * @param cfg configuration object
          */
         private void reconfigureNetwork(AaaConfig cfg) {
-            AaaConfig newCfg;
+
             if (cfg == null) {
                 newCfg = new AaaConfig();
             } else {
@@ -571,14 +559,13 @@
             if (newCfg.radiusSecret() != null) {
                 radiusSecret = newCfg.radiusSecret();
             }
-            if (newCfg.radiusSwitch() != null) {
-                radiusSwitch = newCfg.radiusSwitch();
-            }
-            if (newCfg.radiusPort() != -1) {
-                radiusPort = newCfg.radiusPort();
-            }
-            if (newCfg.radiusServerUdpPort() != -1) {
-                radiusServerPort = newCfg.radiusServerUdpPort();
+
+            radiusConnectionType = newCfg.radiusConnectionType();
+            customizer = newCfg.radiusPktCustomizer();
+
+            if (impl != null) {
+                impl.clearLocalState();
+                impl.initializeLocalState(newCfg);
             }
         }
 
@@ -591,13 +578,9 @@
 
                 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
                 reconfigureNetwork(cfg);
-                radiusSocket.close();
-                executor.shutdownNow();
-                initializeLocalState();
+
                 log.info("Reconfigured");
             }
         }
     }
-
-
 }
diff --git a/src/main/java/org/opencord/aaa/CustomizationInfo.java b/src/main/java/org/opencord/aaa/CustomizationInfo.java
new file mode 100755
index 0000000..d0709f2
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/CustomizationInfo.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onosproject.net.device.DeviceService;
+import org.opencord.sadis.SubscriberAndDeviceInformationService;
+
+//import java.util.Map;
+
+/**
+ * Info required to do customization to packets.
+ */
+public class CustomizationInfo {
+
+    private DeviceService devService;
+
+    private SubscriberAndDeviceInformationService subscriberService;
+
+    public CustomizationInfo(SubscriberAndDeviceInformationService subsService, DeviceService devService) {
+        this.subscriberService = subsService;
+        this.devService = devService;
+    }
+
+    public DeviceService deviceService() {
+        return devService;
+    }
+
+    public SubscriberAndDeviceInformationService subscriberService() {
+        return subscriberService;
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/PacketCustomizer.java b/src/main/java/org/opencord/aaa/PacketCustomizer.java
new file mode 100755
index 0000000..5f48ba4
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/PacketCustomizer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.RADIUS;
+
+import org.onosproject.net.packet.InboundPacket;
+
+/**
+ * Default RADIUS Packet Customization.
+ * Does not change the packet
+ *
+ * Subclasses should implement filling of attributes depending on specifics ofsetup/RADIUS server
+ */
+public class  PacketCustomizer {
+
+    protected CustomizationInfo customInfo;
+
+    public PacketCustomizer(CustomizationInfo info) {
+        this.customInfo = info;
+    }
+
+    /**
+     * Customize the packet as per specific Setup or RADIUS server requirements.
+     *
+     * @param inPkt RADIUS packet to be customized
+     * @param eapPacket Incoming packet containing EAP for which this the RADIUS message is being created
+     * @return Customized RADIUS packet
+     */
+    public RADIUS customizePacket(RADIUS inPkt, InboundPacket eapPacket) {
+        return inPkt;
+    }
+
+    /**
+     * Customize the Ethernet header as per specific Setup or RADIUS server requirements.
+     *
+     * @param inPkt Ethernet packet to be changed
+     * @param eapPacket Incoming packet containing EAP for which this the
+     *                  RADIUS message is being created
+     * @return Changed Ethernet packet
+     */
+    public Ethernet customizeEthernetIPHeaders(Ethernet inPkt,
+                                               InboundPacket eapPacket) {
+        return inPkt;
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java b/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java
new file mode 100755
index 0000000..6e256b7
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.RADIUS;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketService;
+
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.SubscriberAndDeviceInformationService;
+
+import org.slf4j.Logger;
+
+import com.google.common.collect.Maps;
+
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Handles communication with the RADIUS server through ports
+ * of the SDN switches.
+ */
+public class PortBasedRadiusCommunicator implements RadiusCommunicator {
+
+    // for verbose output
+    private final Logger log = getLogger(getClass());
+
+    // our unique identifier
+    private ApplicationId appId;
+
+    // to receive Packet-in events that we'll respond to
+    PacketService packetService;
+
+    DeviceService deviceService;
+
+    MastershipService mastershipService;
+
+    SubscriberAndDeviceInformationService subsService;
+
+    // to store local mapping of IP Address and Serial No of Device
+    private Map<Ip4Address, String> ipToSnMap;
+
+    // connect points to the RADIUS server
+    Set<ConnectPoint> radiusConnectPoints;
+
+    // Parsed RADIUS server addresses
+    protected InetAddress radiusIpAddress;
+
+    // RADIUS server TCP port number
+    protected short radiusServerPort;
+
+    protected String radiusMacAddress;
+
+    // NAS IP address
+    protected InetAddress nasIpAddress;
+
+    protected String nasMacAddress;
+
+    // RADIUS server Vlan ID
+    private short radiusVlanID;
+
+    // RADIUS p-bit
+    private byte radiusPBit;
+
+    PacketCustomizer pktCustomizer;
+    AaaManager aaaManager;
+
+    ConnectPoint radiusServerConnectPoint = null;
+
+    InnerMastershipListener changeListener = new InnerMastershipListener();
+    InnerDeviceListener deviceListener = new InnerDeviceListener();
+
+    PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
+                                MastershipService masService, DeviceService devService,
+                                SubscriberAndDeviceInformationService subsService,
+                                PacketCustomizer pktCustomizer, AaaManager aaaManager) {
+        this.appId = appId;
+        this.packetService = pktService;
+        this.mastershipService = masService;
+        this.deviceService = devService;
+        this.subsService = subsService;
+        this.pktCustomizer = pktCustomizer;
+        this.aaaManager = aaaManager;
+
+        ipToSnMap = Maps.newConcurrentMap();
+        mastershipService.addListener(changeListener);
+        deviceService.addListener(deviceListener);
+
+        log.error("Created PortBased");
+    }
+
+    private void initializeLocalState() {
+        synchronized (this) {
+            radiusServerConnectPoint = null;
+            if (radiusConnectPoints != null) {
+                // find a connect point through a device for which we are master
+                for (ConnectPoint cp: radiusConnectPoints) {
+                    if (mastershipService.isLocalMaster(cp.deviceId())) {
+                        if (deviceService.isAvailable(cp.deviceId())) {
+                            radiusServerConnectPoint = cp;
+                        }
+                        log.warn("RADIUS connectPoint selected is {}", cp);
+                        break;
+                    }
+                }
+            }
+
+            log.warn("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
+
+            if (radiusServerConnectPoint == null) {
+                log.error("Master of none, can't send radius Message to server");
+            }
+        }
+    }
+
+    @Override
+    public void initializeLocalState(AaaConfig newCfg) {
+        if (newCfg.nasIp() != null) {
+            nasIpAddress = newCfg.nasIp();
+        }
+        if (newCfg.radiusIp() != null) {
+            radiusIpAddress = newCfg.radiusIp();
+        }
+        if (newCfg.radiusMac() != null) {
+            radiusMacAddress = newCfg.radiusMac();
+        }
+        if (newCfg.nasMac() != null) {
+            nasMacAddress = newCfg.nasMac();
+        }
+
+        radiusServerPort = newCfg.radiusServerUdpPort();
+        radiusVlanID = newCfg.radiusServerVlanId();
+        radiusPBit = newCfg.radiusServerPBit();
+
+        radiusConnectPoints = newCfg.radiusServerConnectPoints();
+
+        initializeLocalState();
+    }
+
+    @Override
+    public void clearLocalState() {}
+
+    @Override
+    public void deactivate() {
+        mastershipService.removeListener(changeListener);
+        deviceService.removeListener(deviceListener);
+    }
+
+    @Override
+    public void requestIntercepts() {
+        TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP);
+        packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
+
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(radiusServerPort));
+        packetService.requestPackets(selectorServer.build(), CONTROL, appId);
+    }
+
+    @Override
+    public void withdrawIntercepts() {
+        TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP);
+        packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
+
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(radiusServerPort));
+        packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
+    }
+
+    @Override
+    public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
+        // create the packet
+        Ethernet ethReply = new Ethernet();
+        ethReply.setSourceMACAddress(nasMacAddress);
+        ethReply.setDestinationMACAddress(radiusMacAddress);
+        ethReply.setEtherType(Ethernet.TYPE_IPV4);
+        ethReply.setVlanID(radiusVlanID);
+        ethReply.setPriorityCode(radiusPBit);
+
+        IPv4 ipv4Packet = new IPv4();
+        ipv4Packet.setTtl((byte) 64);
+        ipv4Packet.setSourceAddress(Ip4Address.
+                valueOf(nasIpAddress).toInt());
+        ipv4Packet.setDestinationAddress(Ip4Address.
+                valueOf(radiusIpAddress).toInt());
+
+        UDP udpPacket = new UDP();
+        udpPacket.setSourcePort(radiusServerPort);
+        udpPacket.setDestinationPort(radiusServerPort);
+
+        udpPacket.setPayload(radiusPacket);
+        ipv4Packet.setPayload(udpPacket);
+        ethReply.setPayload(ipv4Packet);
+
+        // store the IP address and SN of the device, later to be used
+        // for ARP responses
+        String serialNo = deviceService.getDevice(inPkt.
+                receivedFrom().deviceId()).serialNumber();
+
+        SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
+
+        if (deviceInfo == null) {
+            log.error("No Device found with SN {}", serialNo);
+            return;
+        }
+        ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
+
+        // send the message out
+        sendFromRadiusServerPort(pktCustomizer.
+                customizeEthernetIPHeaders(ethReply, inPkt));
+    }
+
+    /**
+     * Sends packet to the RADIUS server using one of the switch ports.
+     *
+     * @param packet Ethernet packet to be sent
+     */
+    private void sendFromRadiusServerPort(Ethernet packet) {
+        if (radiusServerConnectPoint != null) {
+            log.trace("AAA Manager sending Ethernet packet = {}", packet);
+            TrafficTreatment t = DefaultTrafficTreatment.builder()
+                    .setOutput(radiusServerConnectPoint.port()).build();
+            OutboundPacket o = new DefaultOutboundPacket(
+                    radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
+            packetService.emit(o);
+        } else {
+            log.error("Unable to send RADIUS packet, connectPoint is null");
+        }
+    }
+
+    @Override
+    public void handlePacketFromServer(PacketContext context) {
+        // Extract the original Ethernet frame from the packet information
+        InboundPacket pkt = context.inPacket();
+        Ethernet ethPkt = pkt.parsed();
+        if (ethPkt == null) {
+            return;
+        }
+
+        // identify if incoming packet
+        switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
+            case ARP:
+                handleArpPacketFromServer(context);
+                break;
+            case IPV4:
+                handleIPv4PacketFromServer(context);
+                break;
+            default:
+                log.debug("Skipping Ethernet packet type {}",
+                        EthType.EtherType.lookup(ethPkt.getEtherType()));
+        }
+    }
+
+    /**
+     * Handles ARP packets from RADIUS server.
+     *
+     * @param context Context for the packet
+     */
+    private void handleArpPacketFromServer(PacketContext context) {
+        // Extract the original Ethernet frame from the packet information
+        InboundPacket pkt = context.inPacket();
+        Ethernet ethPkt = pkt.parsed();
+        if (ethPkt == null) {
+            return;
+        }
+
+        ARP arpPacket = (ARP) ethPkt.getPayload();
+
+        Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
+                getTargetProtocolAddress());
+
+        String serialNo = ipToSnMap.get(targetAddress);
+        if (serialNo == null) {
+            log.info("No mapping found for ARP reply, target address {}",
+                    targetAddress);
+            return;
+        }
+        MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
+        if (senderMac == null) {
+            log.error("ARP resolution, MAC address not found for SN {}", serialNo);
+            return;
+        }
+
+        ARP arpReply = (ARP) arpPacket.clone();
+        arpReply.setOpCode(ARP.OP_REPLY);
+        arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
+        arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
+        arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
+        arpReply.setSenderHardwareAddress(senderMac.toBytes());
+
+        log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
+
+        // Ethernet Frame.
+        Ethernet ethReply = new Ethernet();
+        ethReply.setSourceMACAddress(senderMac);
+        ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
+        ethReply.setEtherType(Ethernet.TYPE_ARP);
+        ethReply.setVlanID(radiusVlanID);
+        ethReply.setPriorityCode(ethPkt.getPriorityCode());
+
+        ethReply.setPayload(arpReply);
+        sendFromRadiusServerPort(ethReply);
+    }
+
+    /**
+     * Handles IP packets from RADIUS server.
+     *
+     * @param context Context for the packet
+     */
+    private void handleIPv4PacketFromServer(PacketContext context) {
+        // Extract the original Ethernet frame from the packet information
+        InboundPacket pkt = context.inPacket();
+        Ethernet ethPkt = pkt.parsed();
+        if (ethPkt == null) {
+            return;
+        }
+
+        IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
+
+        if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
+            UDP udpPacket = (UDP) ipv4Packet.getPayload();
+
+            if (udpPacket.getSourcePort() == radiusServerPort) {
+                //This packet is RADIUS packet from the server.
+                RADIUS radiusMsg;
+                try {
+                    radiusMsg =
+                            RADIUS.deserializer()
+                                    .deserialize(udpPacket.serialize(),
+                                            8,
+                                            udpPacket.getLength() - 8);
+                    try {
+                        aaaManager.handleRadiusPacket(radiusMsg);
+                    }  catch (StateMachineException sme) {
+                        log.error("Illegal state machine operation", sme);
+                    }
+                } catch (DeserializationException dex) {
+                    log.error("Cannot deserialize packet", dex);
+                }
+            }
+        }
+    }
+
+    /**
+     * Handles Mastership changes for the devices which connect
+     * to the RADIUS server.
+     */
+    private class InnerMastershipListener implements MastershipListener {
+        @Override
+        public void event(MastershipEvent event) {
+            if (radiusServerConnectPoint != null &&
+                    radiusServerConnectPoint.deviceId().
+                            equals(event.subject())) {
+                log.trace("Mastership Event recevived for {}", event.subject());
+                // mastership of the device for our connect point has changed
+                // reselect
+                initializeLocalState();
+            }
+        }
+    }
+
+    /**
+     * Handles Device status change for the devices which connect
+     * to the RADIUS server.
+     */
+    private class InnerDeviceListener implements DeviceListener {
+        @Override
+        public void event(DeviceEvent event) {
+            log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
+            if (radiusServerConnectPoint == null) {
+                switch (event.type()) {
+                    case DEVICE_ADDED:
+                    case DEVICE_AVAILABILITY_CHANGED:
+                        // some device is available check if we can get one
+                        initializeLocalState();
+                        break;
+                    default:
+                        break;
+                }
+                return;
+            }
+            if (radiusServerConnectPoint.deviceId().
+                    equals(event.subject().id())) {
+                switch (event.type()) {
+                    case DEVICE_AVAILABILITY_CHANGED:
+                    case DEVICE_REMOVED:
+                    case DEVICE_SUSPENDED:
+                        // state of our device has changed, check if we need
+                        // to re-select
+                        initializeLocalState();
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/RadiusCommunicator.java b/src/main/java/org/opencord/aaa/RadiusCommunicator.java
new file mode 100755
index 0000000..4809bca
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/RadiusCommunicator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onlab.packet.RADIUS;
+
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+
+/**
+ * Interface to the implementations for RADIUS server side communication.
+ */
+public interface RadiusCommunicator {
+
+    /**
+     * Does initialization required for the implementation to work and applies the
+     * relevant part of the passed configuration.
+     *
+     * @param newCfg : New configuration to be applied
+     */
+    void initializeLocalState(AaaConfig newCfg);
+    /**
+     * Clears up all local state.
+     */
+    void clearLocalState();
+    /**
+     * Shutdown, called when AAA app is deactivated.
+     */
+    void deactivate();
+    /**
+     * Provision intercepts on the switches if needed.
+     */
+    void requestIntercepts();
+    /**
+     * Clear intercepts from the switches if needed.
+     */
+    void withdrawIntercepts();
+    /**
+     * Send RADIUS packet to the RADIUS server.
+     *
+     * @param radiusPacket RADIUS packet to be sent to server.
+     * @param inPkt        Incoming EAPOL packet
+     */
+    void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt);
+    /**
+     * Handle packet from RADIUS server.
+     *
+     * @param context Incoming packet context.
+     */
+    void handlePacketFromServer(PacketContext context);
+}
diff --git a/src/main/java/org/opencord/aaa/SamplePacketCustomizer.java b/src/main/java/org/opencord/aaa/SamplePacketCustomizer.java
new file mode 100755
index 0000000..5e1bad5
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/SamplePacketCustomizer.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.RADIUS;
+import org.onlab.packet.RADIUSAttribute;
+
+
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Port;
+import org.onosproject.net.packet.InboundPacket;
+
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Sample RADIUS Packet Customization.
+ *
+ */
+public class  SamplePacketCustomizer extends PacketCustomizer {
+
+    private final Logger log = getLogger(getClass());
+
+    public SamplePacketCustomizer(CustomizationInfo customInfo) {
+        super(customInfo);
+    }
+
+    /**
+     * Customize the packet as per specific Setup or RADIUS
+     * server requirements.
+     *
+     * @param inPkt RADIUS packet to be customized
+     * @param eapPacket Incoming packet containing EAP for which this the
+     *                  RADIUS message is being created
+     * @return Customized RADIUS packet
+     */
+    @Override
+    public RADIUS customizePacket(RADIUS inPkt, InboundPacket eapPacket) {
+        Port p = customInfo.deviceService().getPort(eapPacket.receivedFrom());
+
+        String id = p.annotations().value(AnnotationKeys.PORT_NAME);
+
+        log.info("Customizing packet Port received for {}", id);
+
+        SubscriberAndDeviceInformation subscriber = customInfo.
+                subscriberService().get(id);
+
+        if (subscriber == null) {
+            log.warn("No subscriber found with id {}", id);
+            return inPkt;
+        }
+
+        String nasPortId = subscriber.nasPortId();
+
+        Ethernet ethPkt = eapPacket.parsed();
+        MacAddress srcMac = ethPkt.getSourceMAC();
+
+        // Get the nasId from subscriber service using the Serial Number
+        String serialNo = customInfo.deviceService().getDevice(eapPacket.
+                receivedFrom().deviceId()).serialNumber();
+
+        log.info("SampleRadiusCustomizer serial = {}", serialNo);
+        SubscriberAndDeviceInformation deviceInfo = customInfo.
+                subscriberService().get(serialNo);
+
+        if (deviceInfo == null) {
+            log.warn("No Device found with SN {}", serialNo);
+            return inPkt;
+        }
+        String nodeName = deviceInfo.nasId();
+
+        log.info("Setting user={} nasId={} nasPort{}", srcMac, nodeName,
+                nasPortId);
+        inPkt.updateAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
+                srcMac.toString().getBytes());
+
+        inPkt.updateAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
+                deviceInfo.ipAddress().toOctets());
+
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_CALLING_STATION_ID,
+                srcMac.toString().getBytes());
+
+        // Check value - 16 was used in PoC2, as per PoC3 TS value should be 15
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_PORT_TYPE,
+                ByteBuffer.allocate(4).putInt(15).array());
+
+        // Check - This may not be needed but was used in PoC2
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_PORT,
+                ByteBuffer.allocate(4).putInt(46178304).array());
+        // Check - If this is needed, worked with this value in PoC2
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_ACCT_SESSION_ID,
+                "023:27:46:00000".getBytes());
+
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_ID,
+                nodeName.getBytes());
+        inPkt.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_PORT_ID,
+                nasPortId.getBytes());
+
+        return inPkt;
+    }
+
+    /**
+     * Customize the Ethernet header as per specific Setup or RADIUS
+     * server requirements.
+     *
+     * @param inPkt Ethernet packet to be changed
+     * @param eapPacket Incoming packet containing EAP for which this the
+     *                  RADIUS message is being created
+     * @return Changed Ethernet packet
+     */
+    public Ethernet customizeEthernetIPHeaders(Ethernet inPkt,
+                                               InboundPacket eapPacket) {
+
+        String serialNo = customInfo.deviceService().getDevice(eapPacket.
+                receivedFrom().deviceId()).serialNumber();
+
+        log.info("SampleRadiusCustomzer customizer serial = {}", serialNo);
+        SubscriberAndDeviceInformation deviceInfo = customInfo.
+                subscriberService().get(serialNo);
+
+        if (deviceInfo == null) {
+            log.warn("No Device found with SN {}", serialNo);
+            return inPkt;
+        }
+
+        inPkt.setSourceMACAddress(deviceInfo.hardwareIdentifier());
+
+        IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
+        ipv4Packet.setSourceAddress(deviceInfo.ipAddress().toString());
+        inPkt.setPayload(ipv4Packet);
+
+        return inPkt;
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/SocketBasedRadiusCommunicator.java b/src/main/java/org/opencord/aaa/SocketBasedRadiusCommunicator.java
new file mode 100755
index 0000000..1f046e4
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/SocketBasedRadiusCommunicator.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.RADIUS;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketService;
+
+import org.slf4j.Logger;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Handles Socket based communication with the RADIUS server.
+ */
+public class SocketBasedRadiusCommunicator implements RadiusCommunicator {
+
+    // for verbose output
+    private final Logger log = getLogger(getClass());
+
+    // our unique identifier
+    private ApplicationId appId;
+
+    // to receive Packet-in events that we'll respond to
+    PacketService packetService;
+
+    // Socket used for UDP communications with RADIUS server
+    private DatagramSocket radiusSocket;
+
+    // Parsed RADIUS server addresses
+    protected InetAddress radiusIpAddress;
+
+    // RADIUS server TCP port number
+    protected short radiusServerPort;
+
+    // Executor for RADIUS communication thread
+    private ExecutorService executor;
+
+    AaaManager aaaManager;
+
+    SocketBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
+                                  AaaManager aaaManager) {
+        this.appId = appId;
+        this.packetService = pktService;
+        this.aaaManager = aaaManager;
+    }
+
+    @Override
+    public void initializeLocalState(AaaConfig newCfg) {
+        if (newCfg.radiusIp() != null) {
+            radiusIpAddress = newCfg.radiusIp();
+        }
+        radiusServerPort = newCfg.radiusServerUdpPort();
+
+        try {
+            radiusSocket = new DatagramSocket(null);
+            radiusSocket.setReuseAddress(true);
+            radiusSocket.bind(new InetSocketAddress(radiusServerPort));
+        } catch (Exception ex) {
+            log.error("Can't open RADIUS socket", ex);
+        }
+
+        executor = Executors.newSingleThreadExecutor(
+                new ThreadFactoryBuilder()
+                        .setNameFormat("AAA-radius-%d").build());
+        executor.execute(radiusListener);
+    }
+
+    @Override
+    public void clearLocalState() {
+        radiusSocket.close();
+        executor.shutdownNow();
+    }
+
+    @Override
+    public void deactivate() { }
+
+    @Override
+    public void requestIntercepts() {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
+        packetService.requestPackets(selector.build(), CONTROL, appId);
+    }
+
+    @Override
+    public void withdrawIntercepts() {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
+        packetService.cancelPackets(selector.build(), CONTROL, appId);
+    }
+
+    @Override
+    public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
+        try {
+            final byte[] data = radiusPacket.serialize();
+            final DatagramSocket socket = radiusSocket;
+
+            DatagramPacket packet =
+                    new DatagramPacket(data, data.length,
+                            radiusIpAddress, radiusServerPort);
+
+            socket.send(packet);
+            log.warn("Packet sent to Radius Server using socket packet = {}", packet);
+        } catch (IOException e) {
+            log.info("Cannot send packet to RADIUS server", e);
+        }
+    }
+
+    @Override
+    public void handlePacketFromServer(PacketContext context) {
+        InboundPacket pkt = context.inPacket();
+        Ethernet ethPkt = pkt.parsed();
+
+        log.trace("Skipping Ethernet packet type {}",
+                EthType.EtherType.lookup(ethPkt.getEtherType()));
+    }
+
+    class RadiusListener implements Runnable {
+
+        @Override
+        public void run() {
+            boolean done = false;
+            int packetNumber = 1;
+
+            log.info("UDP listener thread starting up");
+            RADIUS inboundRadiusPacket;
+            while (!done) {
+                try {
+                    byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH];
+                    DatagramPacket inboundBasePacket =
+                            new DatagramPacket(packetBuffer, packetBuffer.length);
+                    DatagramSocket socket = radiusSocket;
+                    socket.receive(inboundBasePacket);
+                    log.info("Packet #{} received", packetNumber++);
+                    try {
+                        inboundRadiusPacket =
+                                RADIUS.deserializer()
+                                        .deserialize(inboundBasePacket.getData(),
+                                                0,
+                                                inboundBasePacket.getLength());
+                        aaaManager.handleRadiusPacket(inboundRadiusPacket);
+                    } catch (DeserializationException dex) {
+                        log.error("Cannot deserialize packet", dex);
+                    } catch (StateMachineException sme) {
+                        log.error("Illegal state machine operation", sme);
+                    }
+
+                } catch (IOException e) {
+                    log.info("Socket was closed, exiting listener thread");
+                    done = true;
+                }
+            }
+        }
+    }
+
+    RadiusListener radiusListener = new RadiusListener();
+}
diff --git a/src/main/java/org/opencord/aaa/StateMachine.java b/src/main/java/org/opencord/aaa/StateMachine.java
index 323313f..4a00966 100644
--- a/src/main/java/org/opencord/aaa/StateMachine.java
+++ b/src/main/java/org/opencord/aaa/StateMachine.java
@@ -1,6 +1,5 @@
 /*
- *
- * Copyright 2015 AT&T Foundry
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,11 +17,14 @@
 
 package org.opencord.aaa;
 
-import java.util.BitSet;
 import java.util.Map;
 
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
+
+import org.opencord.olt.AccessDeviceService;
+
 import org.slf4j.Logger;
 
 import com.google.common.collect.Maps;
@@ -48,10 +50,9 @@
     static final int TRANSITION_DENY_ACCESS = 3;
     static final int TRANSITION_LOGOFF = 4;
 
-    //map of access identifiers (issued at EAPOL START)
-    static BitSet bitSet = new BitSet();
+    private static AccessDeviceService accessDeviceService;
 
-    private int identifier = -1;
+    private static int identifier = -1;
     private byte challengeIdentifier;
     private byte[] challengeState;
     private byte[] username;
@@ -61,6 +62,8 @@
     private ConnectPoint supplicantConnectpoint;
     private MacAddress supplicantAddress;
     private short vlanId;
+    private VlanId ctag;
+    private byte priorityCode;
 
     private String sessionId = null;
 
@@ -117,6 +120,7 @@
     public static void initializeMaps() {
         sessionIdMap = Maps.newConcurrentMap();
         identifierMap = Maps.newConcurrentMap();
+        identifier = -1;
     }
 
     public static void destroyMaps() {
@@ -124,6 +128,10 @@
         identifierMap = null;
     }
 
+    public static void setAccessDeviceService(AccessDeviceService service) {
+        accessDeviceService = service;
+    }
+
     public static Map<String, StateMachine> sessionIdMap() {
         return sessionIdMap;
     }
@@ -134,15 +142,24 @@
 
     public static StateMachine lookupStateMachineBySessionId(String sessionId) {
         return sessionIdMap.get(sessionId);
-    }    /**
+    }
+
+    public static void deleteStateMachineMapping(StateMachine machine) {
+        identifierMap.entrySet().removeIf(e -> e.getValue().equals(machine));
+    }
+
+    /**
      * State Machine Constructor.
      *
      * @param sessionId   session Id represented by the switch dpid +  port number
+     * @param ctag        C-TAG for this subscriber
      */
-    public StateMachine(String sessionId) {
-        log.info("Creating a new state machine for {}", sessionId);
+    public StateMachine(String sessionId, VlanId ctag) {
+        log.info("Creating a new state machine for {} C-TAG {}", sessionId,
+                ctag);
         this.sessionId = sessionId;
         sessionIdMap.put(sessionId, this);
+        this.ctag = ctag;
     }
 
     /**
@@ -200,6 +217,24 @@
     }
 
     /**
+     * Gets the client's priority Code.
+     *
+     * @return client Priority code
+     */
+    public byte priorityCode() {
+        return priorityCode;
+    }
+
+    /**
+     * Sets the client's priority Code.
+     *
+     * @param priorityCode new client priority Code
+     */
+    public void setPriorityCode(byte priorityCode) {
+        this.priorityCode = priorityCode;
+    }
+
+    /**
      * Gets the client id that is requesting for access.
      *
      * @return The client id.
@@ -209,30 +244,6 @@
     }
 
     /**
-     * Create the identifier for the state machine (happens when goes to STARTED state).
-     */
-    private void createIdentifier() throws StateMachineException {
-        log.debug("Creating Identifier.");
-        int index;
-
-        try {
-            //find the first available spot for identifier assignment
-            index = StateMachine.bitSet.nextClearBit(0);
-
-            //there is a limit of 256 identifiers
-            if (index == 256) {
-                throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
-            }
-        } catch (IndexOutOfBoundsException e) {
-            throw new StateMachineException(e.getMessage());
-        }
-
-        log.info("Assigning identifier {}", index);
-        StateMachine.bitSet.set(index);
-        this.identifier = index;
-    }
-
-    /**
      * Set the challenge identifier and the state issued by the RADIUS.
      *
      * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
@@ -325,21 +336,12 @@
      *
      * @return The state machine identifier.
      */
-    public byte identifier() {
-        return (byte) this.identifier;
+    public synchronized byte identifier() {
+        identifier = (identifier + 1) % 255;
+        identifierMap.put(identifier, this);
+        return (byte) identifier;
     }
 
-
-    protected void deleteIdentifier() {
-        if (this.identifier != -1) {
-            log.info("Freeing up " + this.identifier);
-            //this state machine should be deleted and free up the identifier
-            StateMachine.bitSet.clear(this.identifier);
-            this.identifier = -1;
-        }
-    }
-
-
     /**
      * Move to the next state.
      *
@@ -359,8 +361,7 @@
         states[currentState].start();
         //move to the next state
         next(TRANSITION_START);
-        createIdentifier();
-        identifierMap.put(identifier, this);
+        identifier = this.identifier();
     }
 
     /**
@@ -386,9 +387,15 @@
         //move to the next state
         next(TRANSITION_AUTHORIZE_ACCESS);
 
-        // TODO: put in calls to launch vSG here
+        if (accessDeviceService != null) {
+            log.info("Provisioning subscriber at {} with C-TAG {}",
+                    supplicantConnectpoint(), ctag);
+            accessDeviceService.provisionSubscriber(supplicantConnectpoint(),
+                                                    ctag);
+        }
 
-        deleteIdentifier();
+        // Clear mapping
+        deleteStateMachineMapping(this);
     }
 
     /**
@@ -401,7 +408,8 @@
         states[currentState].radiusDenied();
         //move to the next state
         next(TRANSITION_DENY_ACCESS);
-        deleteIdentifier();
+        // Clear mappings
+        deleteStateMachineMapping(this);
     }
 
     /**
diff --git a/src/main/java/org/opencord/aaa/StateMachineException.java b/src/main/java/org/opencord/aaa/StateMachineException.java
index d132c2a..7e2e3ca 100644
--- a/src/main/java/org/opencord/aaa/StateMachineException.java
+++ b/src/main/java/org/opencord/aaa/StateMachineException.java
@@ -1,6 +1,5 @@
 /*
- *
- * Copyright 2015 AT&T Foundry
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/main/java/org/opencord/aaa/StateMachineInvalidTransitionException.java b/src/main/java/org/opencord/aaa/StateMachineInvalidTransitionException.java
index a97b02f..cfd949a 100644
--- a/src/main/java/org/opencord/aaa/StateMachineInvalidTransitionException.java
+++ b/src/main/java/org/opencord/aaa/StateMachineInvalidTransitionException.java
@@ -1,6 +1,5 @@
 /*
- *
- * Copyright 2015 AT&T Foundry
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/test/java/org/opencord/aaa/AaaManagerTest.java b/src/test/java/org/opencord/aaa/AaaManagerTest.java
index 4c1f7b2..dadcc71 100644
--- a/src/test/java/org/opencord/aaa/AaaManagerTest.java
+++ b/src/test/java/org/opencord/aaa/AaaManagerTest.java
@@ -29,6 +29,7 @@
 import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.packet.InboundPacket;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -47,7 +48,7 @@
     private AaaManager aaaManager;
 
     class AaaManagerWithoutRadiusServer extends AaaManager {
-        protected void sendRadiusPacket(RADIUS radiusPacket) {
+        protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
             savePacket(radiusPacket);
         }
     }
@@ -204,7 +205,7 @@
 
         RADIUS radiusCodeAccessChallengePacket =
                 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
-        aaaManager.radiusListener.handleRadiusPacket(radiusCodeAccessChallengePacket);
+        aaaManager.handleRadiusPacket(radiusCodeAccessChallengePacket);
 
         Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
         checkRadiusPacket(aaaManager, radiusChallengeMD5Packet, EAP.ATTR_MD5);
@@ -221,7 +222,7 @@
         RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
 
         checkRadiusPacketFromSupplicant(responseMd5RadiusPacket);
-        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 0));
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 3));
         assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
 
         //  State machine should be in pending state
@@ -233,7 +234,7 @@
 
         RADIUS successPacket =
                 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
-        aaaManager.radiusListener.handleRadiusPacket((successPacket));
+        aaaManager.handleRadiusPacket((successPacket));
         Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
 
         checkRadiusPacket(aaaManager, supplicantSuccessPacket, EAP.SUCCESS);
diff --git a/src/test/java/org/opencord/aaa/StateMachineTest.java b/src/test/java/org/opencord/aaa/StateMachineTest.java
index f87d72f..3f95134 100644
--- a/src/test/java/org/opencord/aaa/StateMachineTest.java
+++ b/src/test/java/org/opencord/aaa/StateMachineTest.java
@@ -1,6 +1,5 @@
 /*
- *
- * Copyright 2015 AT&T Foundry
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,13 +17,13 @@
 package org.opencord.aaa;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
+import org.onlab.packet.VlanId;
 
 public class StateMachineTest {
     StateMachine stateMachine = null;
@@ -32,15 +31,13 @@
     @Before
     public void setUp() {
         System.out.println("Set Up.");
-        StateMachine.bitSet.clear();
         StateMachine.initializeMaps();
-        stateMachine = new StateMachine("session0");
+        stateMachine = new StateMachine("session0", VlanId.vlanId((short) 2));
     }
 
     @After
     public void tearDown() {
         System.out.println("Tear Down.");
-        StateMachine.bitSet.clear();
         StateMachine.destroyMaps();
         stateMachine = null;
     }
@@ -216,62 +213,6 @@
         assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
     }
 
-
-    @Test
-    public void testIdentifierAvailability() throws StateMachineException {
-        System.out.println("======= IDENTIFIER TEST =======.");
-        byte identifier = stateMachine.identifier();
-        System.out.println("State: " + stateMachine.state());
-        System.out.println("Identifier: " + Byte.toUnsignedInt(identifier));
-        Assert.assertEquals(-1, identifier);
-        stateMachine.start();
-
-
-        StateMachine sm247 = null;
-        StateMachine sm3 = null;
-
-
-        //create 255 others state machines
-        for (int i = 1; i <= 255; i++) {
-                StateMachine sm = new StateMachine("session" + i);
-                sm.start();
-                byte id = sm.identifier();
-                Assert.assertEquals(i, Byte.toUnsignedInt(id));
-                if (i == 3) {
-                    sm3 = sm;
-                    System.out.println("SM3: " + sm3.toString());
-                }
-                if (i == 247) {
-                    sm247 = sm;
-                    System.out.println("SM247: " + sm247.toString());
-                }
-        }
-
-        //simulate the state machine for a specific session and logoff so we can free up a spot for an identifier
-        //let's choose identifier 247 then we free up 3
-        Assert.assertNotNull(sm247);
-        sm247.requestAccess();
-        sm247.authorizeAccess();
-        sm247.logoff();
-
-        Assert.assertNotNull(sm3);
-        sm3.requestAccess();
-        sm3.authorizeAccess();
-        sm3.logoff();
-
-        StateMachine otherSM3 = new StateMachine("session3b");
-        otherSM3.start();
-        otherSM3.requestAccess();
-        byte id3 = otherSM3.identifier();
-        Assert.assertEquals(3, Byte.toUnsignedInt(id3));
-
-        StateMachine otherSM247 = new StateMachine("session247b");
-        otherSM247.start();
-        otherSM247.requestAccess();
-        byte id247 = otherSM247.identifier();
-        Assert.assertEquals(247, Byte.toUnsignedInt(id247));
-    }
-
     @Test
     public void testSessionIdLookups() {
         String sessionId1 = "session1";
@@ -285,8 +226,8 @@
                 StateMachine.lookupStateMachineBySessionId(sessionId2);
         assertNull(machine2ShouldBeNull);
 
-        StateMachine stateMachine1 = new StateMachine(sessionId1);
-        StateMachine stateMachine2 = new StateMachine(sessionId2);
+        StateMachine stateMachine1 = new StateMachine(sessionId1, VlanId.vlanId((short) 2));
+        StateMachine stateMachine2 = new StateMachine(sessionId2, VlanId.vlanId((short) 2));
 
         assertEquals(stateMachine1,
                      StateMachine.lookupStateMachineBySessionId(sessionId1));
@@ -307,9 +248,9 @@
                 StateMachine.lookupStateMachineById((byte) 2);
         assertNull(machine2ShouldBeNull);
 
-        StateMachine stateMachine1 = new StateMachine(sessionId1);
+        StateMachine stateMachine1 = new StateMachine(sessionId1, VlanId.vlanId((short) 2));
         stateMachine1.start();
-        StateMachine stateMachine2 = new StateMachine(sessionId2);
+        StateMachine stateMachine2 = new StateMachine(sessionId2, VlanId.vlanId((short) 2));
         stateMachine2.start();
 
         assertEquals(stateMachine1,