[SEBA-37] Added 11 Statistics counters for Accounting Server operations

Change-Id: I7dda193129e5dffe62907775aaf8dfdc5b3f1bf1
diff --git a/api/src/main/java/org/opencord/aaa/AaaConfig.java b/api/src/main/java/org/opencord/aaa/AaaConfig.java
index 3072ff5..2c4aa7b 100644
--- a/api/src/main/java/org/opencord/aaa/AaaConfig.java
+++ b/api/src/main/java/org/opencord/aaa/AaaConfig.java
@@ -85,7 +85,6 @@
     // 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.
diff --git a/api/src/main/java/org/opencord/aaa/AaaStatistics.java b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
new file mode 100644
index 0000000..9764427
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2018-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 java.util.concurrent.atomic.AtomicLong;
+
+public class AaaStatistics {
+    //Number of access accept packets sent to the server
+    private AtomicLong acceptResponsesRx = new AtomicLong();
+    //Number of access reject packets sent to the server
+    private AtomicLong rejectResponsesRx = new AtomicLong();
+    //Number of access challenge packets sent to the server
+    private AtomicLong challengeResponsesRx = new AtomicLong();
+    //Number of access request packets sent to the server
+    private AtomicLong accessRequestsTx = new AtomicLong();
+    //Number of access request packets pending a response from the server
+    private AtomicLong pendingRequests = new AtomicLong();
+    //Number of packets of an unknown RADIUS type received from the accounting server
+    private AtomicLong unknownTypeRx = new AtomicLong();
+    //Number of access response packets received from the server with an invalid validator
+    private AtomicLong invalidValidatorsRx = new AtomicLong();
+    //Number of dropped packets received from the accounting server
+    private AtomicLong droppedResponsesRx = new AtomicLong();
+    //Number of malformed access response packets received from the server
+    private AtomicLong malformedResponsesRx = new AtomicLong();
+    //Number of packets received from an unknown server
+    private AtomicLong unknownServerRx = new AtomicLong();
+    //Roundtrip packet time to the accounting server
+    private AtomicLong requestRttMilis = new AtomicLong();
+    //Number of access request packets retransmitted to the server
+    private AtomicLong requestReTx = new AtomicLong();
+    //Number of sessions expired
+    private AtomicLong numberOfSessionsExpired = new AtomicLong();
+
+    public Long getRequestReTx() {
+        return requestReTx.get();
+    }
+
+    public void setRequestRttMilis(AtomicLong requestRttMilis) {
+        this.requestRttMilis = requestRttMilis;
+    }
+
+    public Long getUnknownServerRx() {
+        return unknownServerRx.get();
+    }
+
+    public Long getRequestRttMilis() {
+        return requestRttMilis.get();
+    }
+
+    public Long getMalformedResponsesRx() {
+        return malformedResponsesRx.get();
+    }
+
+    public Long getDroppedResponsesRx() {
+        return droppedResponsesRx.get();
+    }
+
+    public Long getInvalidValidatorsRx() {
+        return invalidValidatorsRx.get();
+    }
+
+    public Long getAcceptResponsesRx() {
+        return acceptResponsesRx.get();
+    }
+
+    public Long getRejectResponsesRx() {
+        return rejectResponsesRx.get();
+    }
+
+    public Long getChallengeResponsesRx() {
+        return challengeResponsesRx.get();
+    }
+
+    public Long getAccessRequestsTx() {
+        return accessRequestsTx.get();
+    }
+
+    public Long getPendingRequests() {
+        return pendingRequests.get();
+    }
+
+    public Long getUnknownTypeRx() {
+        return unknownTypeRx.get();
+    }
+
+    public void increaseAcceptResponsesRx() {
+        acceptResponsesRx.incrementAndGet();
+    }
+
+    public void increaseRejectResponsesRx() {
+        rejectResponsesRx.incrementAndGet();
+    }
+
+    public void increaseChallengeResponsesRx() {
+        challengeResponsesRx.incrementAndGet();
+    }
+
+    public void increaseAccessRequestsTx() {
+        accessRequestsTx.incrementAndGet();
+    }
+
+    public void increaseRequestReTx() {
+        requestReTx.incrementAndGet();
+    }
+
+    public void increaseOrDecreasePendingRequests(boolean isIncrement) {
+        if (isIncrement) {
+            pendingRequests.incrementAndGet();
+        } else {
+            pendingRequests.decrementAndGet();
+        }
+    }
+
+    public void increaseUnknownTypeRx() {
+        unknownTypeRx.incrementAndGet();
+    }
+
+    public void increaseMalformedResponsesRx() {
+        malformedResponsesRx.incrementAndGet();
+    }
+
+    public void increaseInvalidValidatorsRx() {
+        invalidValidatorsRx.incrementAndGet();
+    }
+
+    public void incrementUnknownServerRx() {
+        unknownServerRx.incrementAndGet();
+    }
+
+    public void incrementNumberOfSessionsExpired() {
+        numberOfSessionsExpired.incrementAndGet();
+    }
+
+    public void countDroppedResponsesRx() {
+        long numberOfDroppedPackets = invalidValidatorsRx.get();
+        numberOfDroppedPackets += unknownTypeRx.get();
+        numberOfDroppedPackets += malformedResponsesRx.get();
+        numberOfDroppedPackets += numberOfSessionsExpired.get();
+        this.droppedResponsesRx = new AtomicLong(numberOfDroppedPackets);
+    }
+}
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsDelegate.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsDelegate.java
new file mode 100644
index 0000000..b82e82f
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2018-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.store.StoreDelegate;
+
+
+/**
+ * Delegate for Authentication Statistics.
+ */
+
+public interface AuthenticationStatisticsDelegate extends StoreDelegate<AuthenticationStatisticsEvent> {
+}
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java
new file mode 100644
index 0000000..11c00e3
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018-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.event.AbstractEvent;
+/**
+ * Event indicating the Accounting Data of AAA.
+ */
+public class AuthenticationStatisticsEvent extends
+        AbstractEvent<AuthenticationStatisticsEvent.Type, AaaStatistics> {
+/**
+ * Accounting data.
+ * AuthenticationMetrixEvent event type.
+ */
+    public enum Type {
+        /**
+         * signifies that the Authentication Metrix Event stats has been updated.
+         */
+        STATS_UPDATE
+    }
+    public AuthenticationStatisticsEvent(Type type, AaaStatistics stats) {
+        super(type, stats);
+    }
+}
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEventListener.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEventListener.java
new file mode 100644
index 0000000..5cef070
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEventListener.java
@@ -0,0 +1,26 @@
+/*
+ * 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.event.EventListener;
+
+/**
+ * Listener for accounting events.
+ */
+public interface AuthenticationStatisticsEventListener extends
+        EventListener<AuthenticationStatisticsEvent> {
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java
new file mode 100644
index 0000000..bdb9f2d
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018-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.event.ListenerService;
+
+/**
+ * Service for interacting with accounting module.
+ */
+
+public interface AuthenticationStatisticsService extends
+        ListenerService<AuthenticationStatisticsEvent, AuthenticationStatisticsEventListener> {
+
+    /**
+     * Returns AaaStatistics object.
+     *
+     * @return AaaStatistics
+    */
+    public AaaStatistics getAaaStats();
+
+    /**
+     * Returns AuthenticationStatisticsDelegate object.
+     *
+     * @return AuthenticationStatisticsDelegate
+    */
+    public AuthenticationStatisticsDelegate getStatsDelegate();
+
+    /**
+     * Handle the roundTrip time of Radius Packet.
+     *
+     * @param identifier identifier of incoming radius packet
+    */
+    public void handleRoundtripTime(byte identifier);
+
+    /**
+     * Calculate average roundTrip time of multiple Packets.
+    */
+    public void calculatePacketRoundtripTime();
+
+    /**
+     * Put the identifier value to map.
+     *
+     * @param identifier identifier of incoming radius packet
+    */
+    public void putOutgoingIdentifierToMap(byte identifier);
+}
diff --git a/app/pom.xml b/app/pom.xml
index 0a191db..4362b36 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -72,9 +72,18 @@
                 <groupId>org.onosproject</groupId>
                 <artifactId>onos-maven-plugin</artifactId>
                 <version>1.11</version>
+                <executions>
+                    <execution>
+                        <id>cfg</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                           <goal>cfg</goal>
+                        </goals>
+                    </execution>
+                </executions>
             </plugin>
         </plugins>
     </build>
 
 
-</project>
\ No newline at end of file
+</project>
diff --git a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
index 3e75f17..bcd4cfa 100755
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -19,12 +19,16 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.util.Map;
+import java.util.Dictionary;
+import java.util.HashSet;
 
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
@@ -36,6 +40,8 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.event.AbstractListenerManager;
@@ -62,14 +68,23 @@
 import org.opencord.aaa.AuthenticationEvent;
 import org.opencord.aaa.AuthenticationEventListener;
 import org.opencord.aaa.AuthenticationService;
+import org.opencord.aaa.AuthenticationStatisticsEvent;
+import org.opencord.aaa.AuthenticationStatisticsService;
 import org.opencord.aaa.RadiusCommunicator;
 import org.opencord.aaa.StateMachineDelegate;
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Modified;
 import org.osgi.service.component.annotations.Activate;
 import org.slf4j.Logger;
+import com.google.common.base.Strings;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 /**
  * AAA application for ONOS.
  */
@@ -101,10 +116,21 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
-    protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected AuthenticationStatisticsService aaaStatisticsManager;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    protected AuthenticationStatisticsEventPublisher authenticationStatisticsPublisher;
+    protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
+    private static final int DEFAULT_REPEAT_DELAY = 20;
+    @Property(name = "statisticsGenerationEvent", intValue = DEFAULT_REPEAT_DELAY,
+              label = "statisticsGenerationEvent")
+    private int statisticsGenerationEvent = DEFAULT_REPEAT_DELAY;
+
     // NAS IP address
     protected InetAddress nasIpAddress;
 
@@ -146,6 +172,11 @@
     // latest configuration
     AaaConfig newCfg;
 
+    ScheduledFuture<?> scheduledFuture;
+
+    ScheduledExecutorService executor;
+    String configuredAaaServerAddress;
+    HashSet<Byte> outPacketSet = new HashSet<Byte>();
     // Configuration properties factory
     private final ConfigFactory factory =
             new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
@@ -197,48 +228,58 @@
     }
 
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
         appId = coreService.registerApplication(APP_NAME);
         eventDispatcher.addSink(AuthenticationEvent.class, listenerRegistry);
         netCfgService.addListener(cfgListener);
         netCfgService.registerConfigFactory(factory);
+        cfgService.registerProperties(getClass());
+        modified(context);
         subsService = sadisService.getSubscriberInfoService();
         customInfo = new CustomizationInfo(subsService, deviceService);
         cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
         log.info("Starting with config {} {}", this, newCfg);
-
         configureRadiusCommunication();
-
         // register our event handler
         packetService.addProcessor(processor, PacketProcessor.director(2));
-
-
         StateMachine.initializeMaps();
         StateMachine.setDelegate(delegate);
-
         impl.initializeLocalState(newCfg);
-
         impl.requestIntercepts();
-
         deviceService.addListener(deviceListener);
+        getConfiguredAaaServerAddress();
+        authenticationStatisticsPublisher =
+                new AuthenticationStatisticsEventPublisher();
+        executor = Executors.newScheduledThreadPool(1);
+        scheduledFuture = executor.scheduleAtFixedRate(authenticationStatisticsPublisher,
+                0, statisticsGenerationEvent, TimeUnit.SECONDS);
 
         log.info("Started");
     }
 
-
     @Deactivate
-    public void deactivate() {
+    public void deactivate(ComponentContext context) {
         impl.withdrawIntercepts();
         packetService.removeProcessor(processor);
         netCfgService.removeListener(cfgListener);
+        cfgService.unregisterProperties(getClass(), false);
         StateMachine.unsetDelegate(delegate);
         StateMachine.destroyMaps();
         impl.deactivate();
         deviceService.removeListener(deviceListener);
         eventDispatcher.removeSink(AuthenticationEvent.class);
+        scheduledFuture.cancel(true);
+        executor.shutdown();
         log.info("Stopped");
     }
 
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+       String s = Tools.get(properties, "statisticsGenerationEvent");
+    statisticsGenerationEvent = Strings.isNullOrEmpty(s) ? DEFAULT_REPEAT_DELAY : Integer.parseInt(s.trim());
+    }
+
     private void configureRadiusCommunication() {
         if (radiusConnectionType.toLowerCase().equals("socket")) {
             impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
@@ -265,6 +306,32 @@
         }
     }
 
+    private void getConfiguredAaaServerAddress() {
+        try {
+            InetAddress address;
+            if (newCfg.radiusHostName() != null) {
+                address = InetAddress.getByName(newCfg.radiusHostName());
+            } else {
+                 address = newCfg.radiusIp();
+            }
+
+            configuredAaaServerAddress = address.getHostAddress();
+        } catch (UnknownHostException uhe) {
+            log.warn("Unable to resolve host {}", newCfg.radiusHostName());
+        }
+    }
+
+    private void checkReceivedPacketForValidValidator(RADIUS radiusPacket) {
+        if (!radiusPacket.checkMessageAuthenticator(radiusSecret)) {
+            aaaStatisticsManager.getAaaStats().increaseInvalidValidatorsRx();
+        }
+    }
+    public void checkForPacketFromUnknownServer(String hostAddress) {
+            if (!hostAddress.equals(configuredAaaServerAddress)) {
+                 aaaStatisticsManager.getAaaStats().incrementUnknownServerRx();
+            }
+    }
+
     /**
      * Send RADIUS packet to the RADIUS server.
      *
@@ -272,6 +339,9 @@
      * @param inPkt        Incoming EAPOL packet
      */
     protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
+        outPacketSet.add(radiusPacket.getIdentifier());
+        aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(true);
+        aaaStatisticsManager.getAaaStats().increaseAccessRequestsTx();
         impl.sendRadiusPacket(radiusPacket, inPkt);
     }
 
@@ -291,12 +361,17 @@
         if (stateMachine == null) {
             log.error("Invalid packet identifier {}, could not find corresponding "
                     + "state machine ... exiting", radiusPacket.getIdentifier());
+            aaaStatisticsManager.getAaaStats().incrementNumberOfSessionsExpired();
+            aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
             return;
         }
-
         EAP eapPayload;
         Ethernet eth;
-
+        checkReceivedPacketForValidValidator(radiusPacket);
+        if (outPacketSet.contains(radiusPacket.getIdentifier())) {
+            aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(false);
+            outPacketSet.remove(new Byte(radiusPacket.getIdentifier()));
+        }
         switch (radiusPacket.getCode()) {
             case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
@@ -314,6 +389,7 @@
                         eapPayload, stateMachine.priorityCode());
                 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                aaaStatisticsManager.getAaaStats().increaseChallengeResponsesRx();
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
@@ -331,7 +407,7 @@
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
                 stateMachine.authorizeAccess();
-
+                aaaStatisticsManager.getAaaStats().increaseAcceptResponsesRx();
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_REJECT:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
@@ -356,11 +432,13 @@
                 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
                 stateMachine.denyAccess();
-
+                aaaStatisticsManager.getAaaStats().increaseRejectResponsesRx();
                 break;
             default:
                 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
+                aaaStatisticsManager.getAaaStats().increaseUnknownTypeRx();
         }
+        aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
     }
 
     /**
@@ -473,7 +551,6 @@
                 log.debug("Using existing state-machine for sessionId: {}", sessionId);
             }
 
-
             switch (eapol.getEapolType()) {
                 case EAPOL.EAPOL_START:
                     log.debug("EAP packet: EAPOL_START");
@@ -523,6 +600,9 @@
                             sendRadiusPacket(radiusPayload, inPacket);
 
                             // change the state to "PENDING"
+                            if (stateMachine.state() == StateMachine.STATE_PENDING) {
+                                aaaStatisticsManager.getAaaStats().increaseRequestReTx();
+                            }
                             stateMachine.requestAccess();
                             break;
                         case EAP.ATTR_MD5:
@@ -686,4 +766,26 @@
             }
         }
     }
+    private class AuthenticationStatisticsEventPublisher implements Runnable {
+        private final Logger log = getLogger(getClass());
+        public void run() {
+            log.info("Notifying AuthenticationStatisticsEvent");
+            aaaStatisticsManager.calculatePacketRoundtripTime();
+            log.debug("AcceptResponsesRx---" + aaaStatisticsManager.getAaaStats().getAcceptResponsesRx());
+            log.debug("AccessRequestsTx---" + aaaStatisticsManager.getAaaStats().getAccessRequestsTx());
+            log.debug("ChallengeResponsesRx---" + aaaStatisticsManager.getAaaStats().getChallengeResponsesRx());
+            log.debug("DroppedResponsesRx---" + aaaStatisticsManager.getAaaStats().getDroppedResponsesRx());
+            log.debug("InvalidValidatorsRx---" + aaaStatisticsManager.getAaaStats().getInvalidValidatorsRx());
+            log.debug("MalformedResponsesRx---" + aaaStatisticsManager.getAaaStats().getMalformedResponsesRx());
+            log.debug("PendingRequests---" + aaaStatisticsManager.getAaaStats().getPendingRequests());
+            log.debug("RejectResponsesRx---" + aaaStatisticsManager.getAaaStats().getRejectResponsesRx());
+            log.debug("RequestReTx---" + aaaStatisticsManager.getAaaStats().getRequestReTx());
+            log.debug("RequestRttMilis---" + aaaStatisticsManager.getAaaStats().getRequestRttMilis());
+            log.debug("UnknownServerRx---" + aaaStatisticsManager.getAaaStats().getUnknownServerRx());
+            log.debug("UnknownTypeRx---" + aaaStatisticsManager.getAaaStats().getUnknownTypeRx());
+            aaaStatisticsManager.getStatsDelegate().
+                notify(new AuthenticationStatisticsEvent(AuthenticationStatisticsEvent.Type.STATS_UPDATE,
+                    aaaStatisticsManager.getAaaStats()));
+        }
+        }
 }
diff --git a/app/src/main/java/org/opencord/aaa/impl/AaaStatisticsManager.java b/app/src/main/java/org/opencord/aaa/impl/AaaStatisticsManager.java
new file mode 100644
index 0000000..89be1fe
--- /dev/null
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaStatisticsManager.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2018-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.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.AbstractListenerManager;
+import org.opencord.aaa.AaaStatistics;
+import org.opencord.aaa.AuthenticationStatisticsDelegate;
+import org.opencord.aaa.AuthenticationStatisticsEvent;
+import org.opencord.aaa.AuthenticationStatisticsEventListener;
+import org.opencord.aaa.AuthenticationStatisticsService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+
+@Service
+@Component(immediate = true)
+public class AaaStatisticsManager
+        extends AbstractListenerManager<AuthenticationStatisticsEvent, AuthenticationStatisticsEventListener>
+        implements AuthenticationStatisticsService {
+
+    private AuthenticationStatisticsDelegate statsDelegate;
+
+    public AuthenticationStatisticsDelegate getStatsDelegate() {
+        return statsDelegate;
+    }
+
+    private final Logger log = getLogger(getClass());
+    private AaaStatistics aaaStats;
+    private LinkedList<Long> packetRoundTripTimeList = new LinkedList<Long>();
+    public Map<Byte, Long> outgoingPacketMap = new HashMap<Byte, Long>();
+    private static final int PACKET_COUNT_FOR_AVERAGE_RTT_CALCULATION = 5;
+
+    public AaaStatistics getAaaStats() {
+        return aaaStats;
+    }
+
+    @Activate
+    public void activate() {
+        log.info("Activate aaaStatisticsManager");
+        aaaStats = new AaaStatistics();
+        statsDelegate = new InternalAuthenticationDelegateForStatistics();
+        eventDispatcher.addSink(AuthenticationStatisticsEvent.class, listenerRegistry);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        eventDispatcher.removeSink(AuthenticationStatisticsEvent.class);
+    }
+
+    public void handleRoundtripTime(byte inPacketIdentifier) {
+        long inTimeInMilis = System.currentTimeMillis();
+        if (outgoingPacketMap.containsKey(inPacketIdentifier)) {
+            if (packetRoundTripTimeList.size() > PACKET_COUNT_FOR_AVERAGE_RTT_CALCULATION) {
+                packetRoundTripTimeList.removeFirst();
+            }
+            packetRoundTripTimeList.add(inTimeInMilis - outgoingPacketMap.get(inPacketIdentifier));
+        }
+    }
+
+    public void calculatePacketRoundtripTime() {
+        if (packetRoundTripTimeList.size() > 0) {
+            long avg = (long) packetRoundTripTimeList.stream().mapToLong(i -> i).average().getAsDouble();
+            aaaStats.setRequestRttMilis(new AtomicLong(avg));
+        }
+    }
+
+    public void putOutgoingIdentifierToMap(byte outPacketIdentifier) {
+        outgoingPacketMap.put(outPacketIdentifier, System.currentTimeMillis());
+    }
+
+    /**
+     *Delegate allowing the StateMachine to notify us of events.
+     */
+    private class InternalAuthenticationDelegateForStatistics implements AuthenticationStatisticsDelegate {
+        @Override
+        public void notify(AuthenticationStatisticsEvent authenticationStatisticsEvent) {
+            log.debug("Authentication Statistics event {} for {}", authenticationStatisticsEvent.type(),
+                    authenticationStatisticsEvent.subject());
+            post(authenticationStatisticsEvent);
+        }
+    }
+}
diff --git a/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java b/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
index e1b8bb9..2967a14 100755
--- a/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
@@ -109,7 +109,6 @@
 
     PacketCustomizer pktCustomizer;
     AaaManager aaaManager;
-
     ConnectPoint radiusServerConnectPoint = null;
 
     InnerMastershipListener changeListener = new InnerMastershipListener();
@@ -264,7 +263,7 @@
             return;
         }
         ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
-
+        aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
         // send the message out
         sendFromRadiusServerPort(pktCustomizer.
                 customizeEthernetIPHeaders(ethReply, inPkt));
@@ -390,6 +389,7 @@
                                             8,
                                             udpPacket.getLength() - 8);
                     try {
+                        aaaManager.aaaStatisticsManager.handleRoundtripTime(radiusMsg.getIdentifier());
                         aaaManager.handleRadiusPacket(radiusMsg);
                     }  catch (StateMachineException sme) {
                         log.error("Illegal state machine operation", sme);
diff --git a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
index e2bce35..0324083 100755
--- a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
@@ -148,6 +148,7 @@
                     log.trace("Sending packet {} to Radius Server {}:{} using socket",
                               radiusPacket, address, radiusServerPort);
                 }
+                aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
                 socket.send(packet);
             } catch (UnknownHostException uhe) {
                 log.warn("Unable to resolve host {}", radiusHost);
@@ -184,6 +185,7 @@
                             new DatagramPacket(packetBuffer, packetBuffer.length);
                     DatagramSocket socket = radiusSocket;
                     socket.receive(inboundBasePacket);
+                    aaaManager.checkForPacketFromUnknownServer(inboundBasePacket.getAddress().getHostAddress());
                     log.debug("Packet #{} received", packetNumber++);
                     try {
                         inboundRadiusPacket =
@@ -191,8 +193,10 @@
                                         .deserialize(inboundBasePacket.getData(),
                                                 0,
                                                 inboundBasePacket.getLength());
+                        aaaManager.aaaStatisticsManager.handleRoundtripTime(inboundRadiusPacket.getIdentifier());
                         aaaManager.handleRadiusPacket(inboundRadiusPacket);
                     } catch (DeserializationException dex) {
+                        aaaManager.aaaStatisticsManager.getAaaStats().increaseMalformedResponsesRx();
                         log.error("Cannot deserialize packet", dex);
                     } catch (StateMachineException sme) {
                         log.error("Illegal state machine operation", sme);
diff --git a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
index f9e5f25..0b60c2e 100644
--- a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -63,7 +63,6 @@
 
     private final Logger log = getLogger(getClass());
 
-
     private State[] states = {
             new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
     };
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaIntegrationTest.java b/app/src/test/java/org/opencord/aaa/impl/AaaIntegrationTest.java
index 8ded2f8..0275b13 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaIntegrationTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaIntegrationTest.java
@@ -25,6 +25,7 @@
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
 import org.opencord.aaa.AaaConfig;
+import org.opencord.aaa.impl.AaaTestBase.MockCfgService;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
@@ -60,7 +61,8 @@
         aaa.netCfgService = new TestNetworkConfigRegistry();
         aaa.coreService = new CoreServiceAdapter();
         aaa.packetService = new MockPacketService();
-        aaa.activate();
+        aaa.cfgService = new MockCfgService();
+        aaa.activate(new AaaTestBase.MockComponentContext());
     }
 
     /**
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java b/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
index 8827c1a..7303257 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
@@ -54,13 +54,13 @@
     static final String BAD_IP_ADDRESS = "198.51.100.0";
 
     private AaaManager aaaManager;
+    private AaaStatisticsManager aaaStatisticsManager;
 
     class AaaManagerWithoutRadiusServer extends AaaManager {
         protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
             savePacket(radiusPacket);
         }
     }
-
     /**
      * Mocks the AAAConfig class to force usage of an unroutable address for the
      * RADIUS server.
@@ -92,7 +92,6 @@
 
     public static class TestEventDispatcher extends DefaultEventSinkRegistry
             implements EventDeliveryService {
-
         @Override
         @SuppressWarnings("unchecked")
         public synchronized void post(Event event) {
@@ -136,7 +135,8 @@
         radius.setPayload(eap);
         radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
                             eap.serialize());
-
+        radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH,
+                aaaManager.radiusSecret.getBytes());
         return radius;
     }
 
@@ -165,8 +165,13 @@
         aaaManager.packetService = new MockPacketService();
         aaaManager.deviceService = new TestDeviceService();
         aaaManager.sadisService = new MockSadisService();
+        aaaManager.cfgService = new MockCfgService();
+        aaaStatisticsManager = new AaaStatisticsManager();
+        TestUtils.setField(aaaStatisticsManager, "eventDispatcher", new TestEventDispatcher());
+        aaaStatisticsManager.activate();
+        aaaManager.aaaStatisticsManager = this.aaaStatisticsManager;
         TestUtils.setField(aaaManager, "eventDispatcher", new TestEventDispatcher());
-        aaaManager.activate();
+        aaaManager.activate(new AaaTestBase.MockComponentContext());
     }
 
     /**
@@ -174,7 +179,7 @@
      */
     @After
     public void tearDown() {
-        aaaManager.deactivate();
+        aaaManager.deactivate(new AaaTestBase.MockComponentContext());
     }
 
     /**
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java b/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
index 2e21ea3..b49fbc4 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
@@ -23,7 +23,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
-
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.net.Annotations;
 import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.ConnectPoint;
@@ -42,9 +43,16 @@
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentInstance;
 
 import java.nio.ByteBuffer;
 import java.security.MessageDigest;
+import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -100,6 +108,67 @@
             }
         }
     }
+    class MockComponentContext implements ComponentContext {
+
+                @Override
+                public Dictionary<String, Object> getProperties() {
+                        Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
+                        cfgDict.put("statisticsGenerationEvent", 20);
+                        return cfgDict;
+                }
+
+                @Override
+                public Object locateService(String name) {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public Object locateService(String name, ServiceReference reference) {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public Object[] locateServices(String name) {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public BundleContext getBundleContext() {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public Bundle getUsingBundle() {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public ComponentInstance getComponentInstance() {
+                        // TODO Auto-generated method stub
+                        return null;
+                }
+
+                @Override
+                public void enableComponent(String name) {
+                        // TODO Auto-generated method stub
+                }
+
+                @Override
+                public void disableComponent(String name) {
+                       // TODO Auto-generated method stub
+                }
+
+                @Override
+                public ServiceReference getServiceReference() {
+                       // TODO Auto-generated method stub
+                       return null;
+                }
+    }
 
     /**
      * Mocks the DeviceService.
@@ -173,6 +242,55 @@
         }
     }
 
+    final class MockCfgService implements ComponentConfigService {
+        @Override
+        public Set<String> getComponentNames() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public void registerProperties(Class<?> componentClass) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public void unregisterProperties(Class<?> componentClass, boolean clear) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public Set<ConfigProperty> getProperties(String componentName) {
+           return null;
+        }
+
+        @Override
+        public void setProperty(String componentName, String name, String value) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public void preSetProperty(String componentName, String name, String value) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public void preSetProperty(String componentName, String name, String value, boolean override) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public void unsetProperty(String componentName, String name) {
+            // TODO Auto-generated method stub
+        }
+
+        @Override
+        public ConfigProperty getProperty(String componentName, String attribute) {
+           return null;
+        }
+
+}
+
     final class MockSubService implements BaseInformationService<SubscriberAndDeviceInformation> {
         private final VlanId clientCtag = VlanId.vlanId((short) 999);
         private final VlanId clientStag = VlanId.vlanId((short) 111);