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

Change-Id: I2a51dbf316637e685b7e3c36595a27922c79b23c
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.