Several improvements for AAA
- Skip radius in 802.1x authentication (forgeEapol)
- Avoid deadlock in the IdentifierManager and optimizes locking
- Periodic pruning of the stalled auths
- Protection for the RadiusLister against the exceptions
- Check attributes before getting them
- Offload radius packet to another worker thread
- Improve unit tests
Change-Id: Idc4000dfb0a44f6a7fbc9aeea8ec754659f98545
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 660ad33..d4f0fbb 100644
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -121,6 +121,7 @@
OPERATIONAL_STATUS_SERVER_TIMEOUT + ":Integer=" + OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT,
STATUS_SERVER_MODE + ":String=" + STATUS_SERVER_MODE_DEFAULT,
PACKET_PROCESSOR_THREADS + ":Integer=" + PACKET_PROCESSOR_THREADS_DEFAULT,
+ FORGE_EAPOL_PACKETS + ":Boolean=" + FORGE_EAPOL_PACKETS_DEFAULT,
})
public class AaaManager
extends AbstractListenerManager<AuthenticationEvent, AuthenticationEventListener>
@@ -171,6 +172,12 @@
private int operationalStatusEventGenerationPeriodInSeconds = OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT;
private int operationalStatusServerTimeoutInSeconds = OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT;
protected String operationalStatusEvaluationMode = STATUS_SERVER_MODE_DEFAULT;
+
+ /**
+ * If set to true the RADIUS server won't be involved in authentication.
+ **/
+ private Boolean forgeEapolPackets = FORGE_EAPOL_PACKETS_DEFAULT;
+
/**
* Number of threads used to process the packet.
*/
@@ -329,7 +336,7 @@
getConfiguredAaaServerAddress();
radiusOperationalStatusService.initialize(nasIpAddress.getAddress(), radiusSecret, impl);
serverStatusAndStateMachineTimeoutExecutor = Executors.newScheduledThreadPool(STATE_MACHINE_THREADS,
- groupedThreads("onos/aaa", "aaa-machine-%d", log));
+ groupedThreads("onos/aaa", "machine-%d", log));
scheduledStatusServerChecker = serverStatusAndStateMachineTimeoutExecutor.scheduleAtFixedRate(
new ServerStatusChecker(), 0,
@@ -369,6 +376,9 @@
operationalStatusServerTimeoutInSeconds = Strings.isNullOrEmpty(s) ? OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT
: Integer.parseInt(s.trim());
+ Boolean p = Tools.isPropertyEnabled(properties, FORGE_EAPOL_PACKETS);
+ forgeEapolPackets = (p == null) ? FORGE_EAPOL_PACKETS_DEFAULT : p;
+
s = Tools.get(properties, "operationalStatusEvaluationMode");
String newEvaluationModeString = Strings.isNullOrEmpty(s) ? STATUS_SERVER_MODE_DEFAULT : s.trim();
@@ -392,7 +402,7 @@
packetProcessorExecutor.shutdown();
}
packetProcessorExecutor = newSingleThreadExecutor(
- groupedThreads("onos/aaa", "aaa-packet-%d", log));
+ groupedThreads("onos/aaa", "packet-%d", log));
}
}
@@ -446,7 +456,20 @@
private boolean checkResponseMessageAuthenticator(String key, RADIUS radiusPacket, byte[] requestAuthenticator) {
byte[] newHash = new byte[16];
Arrays.fill(newHash, (byte) 0);
- byte[] messageAuthenticator = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH).getValue();
+ // looking for the attributes - exit if there are no such attributes
+ if (radiusPacket.getAttributeList(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH).isEmpty()) {
+ log.warn("Empty Attribute List for packet {} with identifier {}",
+ radiusPacket, radiusPacket.getIdentifier());
+ return false;
+ }
+ // get the attribute - further verify if it is null or not (not really needed)
+ RADIUSAttribute attribute = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH);
+ if (attribute == null) {
+ log.warn("Null Message Authenticator for packet {} with identifier {}",
+ radiusPacket, radiusPacket.getIdentifier());
+ return false;
+ }
+ byte[] messageAuthenticator = attribute.getValue();
byte[] authenticator = radiusPacket.getAuthenticator();
radiusPacket.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, newHash);
radiusPacket.setAuthenticator(requestAuthenticator);
@@ -495,13 +518,23 @@
*/
public void handleRadiusPacket(RADIUS radiusPacket) {
if (log.isTraceEnabled()) {
- log.trace("Received RADIUS packet {}", radiusPacket);
+ log.trace("Received RADIUS packet {} with identifier {}",
+ radiusPacket, radiusPacket.getIdentifier() & 0xff);
}
if (radiusOperationalStatusService.isRadiusResponseForOperationalStatus(radiusPacket.getIdentifier())) {
+ if (log.isTraceEnabled()) {
+ log.trace("Handling operational status RADIUS packet {} with identifier {}",
+ radiusPacket, radiusPacket.getIdentifier() & 0xff);
+ }
radiusOperationalStatusService.handleRadiusPacketForOperationalStatus(radiusPacket);
return;
}
+ if (log.isTraceEnabled()) {
+ log.trace("Handling actual RADIUS packet for supplicant {} with identifier {}",
+ radiusPacket, radiusPacket.getIdentifier() & 0xff);
+ }
+
RequestIdentifier identifier = RequestIdentifier.of(radiusPacket.getIdentifier());
String sessionId = idManager.getSessionId(identifier);
@@ -638,7 +671,7 @@
stateMachine.vlanId(),
EAPOL.EAPOL_PACKET,
eapPayload, stateMachine.priorityCode());
- log.warn("Send EAP failure message to supplicant {} on dev/port: {}/{}" +
+ log.warn("Send EAP failure message to supplicant on dev/port: {}/{}" +
" with MacAddress {} and Identifier {}", supplicantCp.deviceId(), supplicantCp.port(),
dstMac, stateMachine.challengeIdentifier() & 0xff);
sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint(), false);
@@ -685,8 +718,8 @@
treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
EAPOL eap = ((EAPOL) ethernetPkt.getPayload());
if (log.isTraceEnabled()) {
- log.trace("Sending eapol payload {} enclosed in {} to supplicant at {} with MacAddress {}",
- eap, ethernetPkt, connectPoint, ethernetPkt.getDestinationMAC());
+ log.trace("Sending eapol payload {} to supplicant at {} with MacAddress {}",
+ eap, connectPoint, ethernetPkt.getDestinationMAC());
}
packetService.emit(packet);
if (isChallengeResponse) {
@@ -740,35 +773,48 @@
@Override
public void process(PacketContext context) {
packetProcessorExecutor.execute(() -> {
- // Extract the original Ethernet frame from the packet information
- InboundPacket pkt = context.inPacket();
- if (pkt == null) {
- log.error("InboundPacket is null when parsed from {}", context);
- return;
- }
- Ethernet ethPkt = pkt.parsed();
- if (ethPkt == null) {
- log.error("EthPkt is null when parsed from {}", pkt);
- return;
- }
+ try {
+ // Extract the original Ethernet frame from the packet information
+ InboundPacket pkt = context.inPacket();
+ if (pkt == null) {
+ log.warn("Dropping inbound packet as it can't be parsed (inpacket)");
+ return;
+ }
+ Ethernet ethPkt = pkt.parsed();
+ if (ethPkt == null) {
+ log.warn("Dropping inbound packet as it can't be parsed (ethpacket)");
+ return;
+ }
- // identify if incoming packet comes from supplicant (EAP) or RADIUS
- switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
- case EAPOL:
- if (log.isTraceEnabled()) {
- log.trace("Received EAPOL supplicant packet from dev/port: {} with MacAddress {}",
- context.inPacket().receivedFrom(), ethPkt.getSourceMAC());
- }
- handleSupplicantPacket(context.inPacket());
- break;
- default:
- if (log.isTraceEnabled()) {
- log.trace("Received packet-in from RADIUS server {} in enclosing packet {} from "
- + "dev/port: {} with MacAddress {}", ethPkt, context.inPacket(),
- context.inPacket().receivedFrom(), ethPkt.getSourceMAC());
- }
- // any other packets let the specific implementation handle
- impl.handlePacketFromServer(context);
+ EthType.EtherType pktType;
+ try {
+ short ethType = ethPkt.getEtherType();
+ pktType = EthType.EtherType.lookup(ethType);
+ } catch (Exception e) {
+ log.error("Exception while reading packet type", e);
+ return;
+ }
+
+ // identify if incoming packet comes from supplicant (EAP) or RADIUS
+ switch (pktType) {
+ case EAPOL:
+ if (log.isTraceEnabled()) {
+ log.trace("Received EAPOL supplicant packet from dev/port: {} with MacAddress {}",
+ context.inPacket().receivedFrom(), ethPkt.getSourceMAC());
+ }
+ handleSupplicantPacket(context.inPacket());
+ break;
+ default:
+ // any other packets let the specific implementation handle
+ if (log.isTraceEnabled()) {
+ log.trace("Received packet-in from RADIUS server {} in enclosing packet {} from "
+ + "dev/port: {} with MacAddress {}", ethPkt, context.inPacket(),
+ context.inPacket().receivedFrom(), ethPkt.getSourceMAC());
+ }
+ impl.handlePacketFromServer(context);
+ }
+ } catch (Exception e) {
+ log.error("Error while processing packet", e);
}
});
}
@@ -800,6 +846,107 @@
return radiusPayload;
}
+ private void handleEapolStart(InboundPacket inPacket, StateMachine stateMachine) {
+
+ DeviceId deviceId = inPacket.receivedFrom().deviceId();
+ PortNumber portNumber = inPacket.receivedFrom().port();
+ Ethernet ethPkt = inPacket.parsed();
+ MacAddress srcMac = ethPkt.getSourceMAC();
+
+ log.debug("EAP packet: EAPOL_START from dev/port: {}/{} with MacAddress {}",
+ deviceId, portNumber, srcMac);
+ stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
+ stateMachine.setSupplicantAddress(srcMac);
+ stateMachine.start();
+
+ aaaStatisticsManager.getAaaStats().incrementEapolStartReqRx();
+ //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, stateMachine.priorityCode());
+
+ stateMachine.setVlanId(ethPkt.getVlanID());
+ log.debug("Getting EAP identity from supplicant {}", stateMachine.supplicantAddress().toString());
+ sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint(), false);
+ aaaStatisticsManager.getAaaStats().incrementRequestIdFramesTx();
+ }
+
+ private void hangleEapolLogoff(InboundPacket inPacket, StateMachine stateMachine) {
+
+ DeviceId deviceId = inPacket.receivedFrom().deviceId();
+ PortNumber portNumber = inPacket.receivedFrom().port();
+ Ethernet ethPkt = inPacket.parsed();
+ MacAddress srcMac = ethPkt.getSourceMAC();
+
+ log.debug("EAP packet: EAPOL_LOGOFF from dev/port: {}/{} with MacAddress {}",
+ deviceId, portNumber, srcMac);
+ //posting the machine stat data for current supplicant device.
+ if (stateMachine.getSessionTerminateReason() == null ||
+ stateMachine.getSessionTerminateReason().equals("")) {
+ stateMachine.setSessionTerminateReason(
+ StateMachine.SessionTerminationReasons.SUPPLICANT_LOGOFF.getReason());
+ }
+ AaaSupplicantMachineStats obj = aaaSupplicantStatsManager.getSupplicantStats(stateMachine);
+ aaaSupplicantStatsManager.getMachineStatsDelegate()
+ .notify(new AaaMachineStatisticsEvent(AaaMachineStatisticsEvent.Type.STATS_UPDATE, obj));
+ if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
+ stateMachine.logoff();
+ aaaStatisticsManager.getAaaStats().incrementEapolLogoffRx();
+ }
+ if (stateMachine.state() == StateMachine.STATE_IDLE) {
+ aaaStatisticsManager.getAaaStats().incrementAuthStateIdle();
+ }
+ }
+
+ private void handleForgedEapolChallengeAuth(StateMachine stateMachine) {
+ stateMachine.requestAccess();
+
+ log.info("Forging EAP auth challenge");
+ byte[] challengeState = EapolPacketGenerator.hexStringToByteArray("19056d66190469d738db2f7dc1e02591");
+ EAP eapPayload = EapolPacketGenerator.forgeEapolChallengeAuth();
+
+ Ethernet eth = buildEapolResponse(stateMachine.supplicantAddress(),
+ MacAddress.valueOf(nasMacAddress),
+ stateMachine.vlanId(),
+ EAPOL.EAPOL_PACKET,
+ eapPayload, stateMachine.priorityCode());
+ stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
+
+ ConnectPoint supplicantCp = stateMachine.supplicantConnectpoint();
+ MacAddress dstMac = stateMachine.supplicantAddress();
+ log.info("Send FORGED EAP auth challenge to supplicant {} on dev/port: {}/{} with MacAddress {}",
+ supplicantCp.deviceId(), supplicantCp.port(), dstMac);
+
+ sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint(),
+ true);
+
+ // NOTE do we care about stats?
+ }
+
+ private void handleForgedEapolSuccess(StateMachine stateMachine) {
+ ConnectPoint supplicantCp = stateMachine.supplicantConnectpoint();
+ MacAddress dstMac = stateMachine.supplicantAddress();
+ log.info("Forging EAP auth success");
+ EAP eapPayload = EapolPacketGenerator.forgeEapolSuccess();
+
+ Ethernet eth = buildEapolResponse(stateMachine.supplicantAddress(),
+ MacAddress.valueOf(nasMacAddress),
+ stateMachine.vlanId(),
+ EAPOL.EAPOL_PACKET,
+ eapPayload, stateMachine.priorityCode());
+ log.info("Send FORGED EAP success message to supplicant {} on dev/port: {}/{} with MacAddress {}",
+ supplicantCp.deviceId(), supplicantCp.port(), dstMac);
+ sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint(), false);
+ aaaStatisticsManager.getAaaStats().incrementEapolAuthSuccessTrans();
+
+ stateMachine.authorizeAccess();
+ }
+
+
/**
* Handles PAE packets (supplicant).
*
@@ -826,13 +973,14 @@
byte[] eapPayLoadBuffer = eapol.serialize();
int len = eapPayLoadBuffer.length;
if (len != (HEADER_LENGTH + pktlen)) {
- log.error("Invalid EAPOL pkt length {} (shoudl be {}) for packet {} from dev/port: {}/{} " +
- "with MacAddress {}", len, HEADER_LENGTH + pktlen, eapol, deviceId, portNumber, srcMac);
+ log.warn("Invalid EAPOL pkt length {} (shoudl be {}) for packet {} from dev/port: {}/{} " +
+ "with MacAddress {}, dropping it",
+ len, HEADER_LENGTH + pktlen, eapol, deviceId, portNumber, srcMac);
aaaStatisticsManager.getAaaStats().incrementInvalidBodyLength();
return;
}
if (!VALID_EAPOL_TYPE.contains(eapol.getEapolType())) {
- log.error("Invalid EAPOL Type {} for packet {} from dev/port: {}/{} with MacAddress {}",
+ log.warn("Invalid EAPOL Type {} for packet {} from dev/port: {}/{} with MacAddress {}, dropping it",
eapol.getEapolType(), eapol, deviceId, portNumber, srcMac);
aaaStatisticsManager.getAaaStats().incrementInvalidPktType();
return;
@@ -846,75 +994,34 @@
switch (eapol.getEapolType()) {
case EAPOL.EAPOL_START:
- log.debug("EAP packet: EAPOL_START from dev/port: {}/{} with MacAddress {}",
- deviceId, portNumber, srcMac);
- stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
- stateMachine.setSupplicantAddress(srcMac);
- stateMachine.start();
-
- aaaStatisticsManager.getAaaStats().incrementEapolStartReqRx();
- //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, stateMachine.priorityCode());
-
- stateMachine.setVlanId(ethPkt.getVlanID());
- log.debug("Getting EAP identity from supplicant {} and Identifier {}",
- stateMachine.supplicantAddress().toString(), stateMachine.identifier() & 0xff);
- sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint(), false);
- aaaStatisticsManager.getAaaStats().incrementRequestIdFramesTx();
+ handleEapolStart(inPacket, stateMachine);
break;
case EAPOL.EAPOL_LOGOFF:
- log.debug("EAP packet: EAPOL_LOGOFF from dev/port: {}/{} with MacAddress {}",
- deviceId, portNumber, srcMac);
- //posting the machine stat data for current supplicant device.
- if (stateMachine.getSessionTerminateReason() == null ||
- stateMachine.getSessionTerminateReason().equals("")) {
- stateMachine.setSessionTerminateReason(
- StateMachine.SessionTerminationReasons.SUPPLICANT_LOGOFF.getReason());
- }
- AaaSupplicantMachineStats obj = aaaSupplicantStatsManager.getSupplicantStats(stateMachine);
- aaaSupplicantStatsManager.getMachineStatsDelegate()
- .notify(new AaaMachineStatisticsEvent(AaaMachineStatisticsEvent.Type.STATS_UPDATE, obj));
- if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
- stateMachine.logoff();
- aaaStatisticsManager.getAaaStats().incrementEapolLogoffRx();
- }
- if (stateMachine.state() == StateMachine.STATE_IDLE) {
- aaaStatisticsManager.getAaaStats().incrementAuthStateIdle();
- }
+ hangleEapolLogoff(inPacket, stateMachine);
break;
case EAPOL.EAPOL_PACKET:
// check if this is a Response/Identify or a Response/TLS
EAP eapPacket = (EAP) eapol.getPayload();
Byte identifier = new Byte(eapPacket.getIdentifier());
+
log.debug("EAP packet: EAPOL_PACKET from dev/port: {}/{} with MacAddress {} with Identifier {}",
deviceId, portNumber, srcMac, identifier.doubleValue());
- // get identifier for request and store mapping to session ID
- RequestIdentifier radiusIdentifier = idManager.getNewIdentifier(sessionId);
- if (radiusIdentifier == null) {
- log.error("Cannot get RADIUS Identifier, dropping packet");
- return;
- }
byte dataType = eapPacket.getDataType();
switch (dataType) {
case EAP.ATTR_IDENTITY:
handleAttrIdentity(inPacket, srcMac, deviceId, portNumber,
- eapol, stateMachine, eapPacket, radiusIdentifier);
+ eapol, stateMachine, eapPacket, sessionId);
break;
case EAP.ATTR_MD5:
handleMD5(inPacket, srcMac, deviceId, portNumber, stateMachine,
- eapPacket, identifier, radiusIdentifier);
+ eapPacket, identifier, sessionId);
break;
case EAP.ATTR_TLS:
handleTls(inPacket, srcMac, deviceId, portNumber, stateMachine,
- eapPacket, identifier, radiusIdentifier);
+ eapPacket, identifier, sessionId);
break;
default:
log.warn("Unknown EAP packet type from dev/port: {}/{} with MacAddress {} and " +
@@ -932,44 +1039,65 @@
private void handleAttrIdentity(InboundPacket inPacket, MacAddress srcMac, DeviceId deviceId,
PortNumber portNumber, EAPOL eapol, StateMachine stateMachine,
- EAP eapPacket, RequestIdentifier radiusIdentifier) {
- RADIUS radiusPayload;
- log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY from dev/port: {}/{} with MacAddress {}" +
- " and Identifier {}", deviceId, portNumber, srcMac, eapPacket.getIdentifier() & 0xff);
- //Setting the time of this response from RG, only when its not a re-transmission.
- if (stateMachine.getLastPacketReceivedTime() == 0) {
- stateMachine.setLastPacketReceivedTime(System.currentTimeMillis());
- }
- // request id access to RADIUS
- stateMachine.setUsername(eapPacket.getData());
+ EAP eapPacket, String sessionId) {
- radiusPayload = getRadiusPayload(stateMachine, radiusIdentifier.identifier(), eapPacket);
- radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
- radiusPayload.addMessageAuthenticator(radiusSecret);
+ if (forgeEapolPackets) {
+ handleForgedEapolChallengeAuth(stateMachine);
+ return;
+ } else {
+ // get identifier for request and store mapping to session ID
+ RequestIdentifier radiusIdentifier = idManager.getNewIdentifier(sessionId);
+ if (radiusIdentifier == null) {
+ log.warn("Cannot get identifier supplicant at dev/port: {}/{} " +
+ "with MacAddress {}, dropping packet",
+ deviceId, portNumber, srcMac);
+ return;
+ }
+ log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY from dev/port: {}/{} with MacAddress {}" +
+ " and Identifier {}", deviceId, portNumber, srcMac, eapPacket.getIdentifier() & 0xff);
+ //Setting the time of this response from RG, only when its not a re-transmission.
+ if (stateMachine.getLastPacketReceivedTime() == 0) {
+ stateMachine.setLastPacketReceivedTime(System.currentTimeMillis());
+ }
+ // request id access to RADIUS
+ stateMachine.setUsername(eapPacket.getData());
- if (log.isTraceEnabled()) {
- log.trace("Sending ATTR_IDENTITY packet to RADIUS for supplicant at dev/port: " +
- "{}/{} with MacAddress {} and Identifier {}", deviceId, portNumber,
- srcMac, radiusIdentifier.identifier() & 0xff);
- }
+ RADIUS radiusPayload = getRadiusPayload(stateMachine, radiusIdentifier.identifier(), eapPacket);
+ radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
+ radiusPayload.addMessageAuthenticator(radiusSecret);
- sendRadiusPacket(radiusPayload, inPacket);
- stateMachine.setWaitingForRadiusResponse(true);
- aaaStatisticsManager.getAaaStats().incrementRadiusReqIdTx();
- aaaStatisticsManager.getAaaStats().incrementEapolAtrrIdentity();
- // change the state to "PENDING"
- if (stateMachine.state() == StateMachine.STATE_PENDING) {
- aaaStatisticsManager.getAaaStats().increaseRequestReTx();
- stateMachine.incrementTotalPacketsSent();
- stateMachine.incrementTotalOctetSent(eapol.getPacketLength());
+ if (log.isTraceEnabled()) {
+ log.trace("Sending ATTR_IDENTITY packet to RADIUS for supplicant at dev/port: " +
+ "{}/{} with MacAddress {} and Identifier {}", deviceId, portNumber,
+ srcMac, radiusIdentifier.identifier() & 0xff);
+ }
+
+ sendRadiusPacket(radiusPayload, inPacket);
+ stateMachine.setWaitingForRadiusResponse(true);
+ aaaStatisticsManager.getAaaStats().incrementRadiusReqIdTx();
+ aaaStatisticsManager.getAaaStats().incrementEapolAtrrIdentity();
+ // change the state to "PENDING"
+ if (stateMachine.state() == StateMachine.STATE_PENDING) {
+ aaaStatisticsManager.getAaaStats().increaseRequestReTx();
+ stateMachine.incrementTotalPacketsSent();
+ stateMachine.incrementTotalOctetSent(eapol.getPacketLength());
+ }
+ stateMachine.requestAccess();
}
- stateMachine.requestAccess();
}
private void handleMD5(InboundPacket inPacket, MacAddress srcMac, DeviceId deviceId,
PortNumber portNumber, StateMachine stateMachine, EAP eapPacket,
- Byte identifier, RequestIdentifier radiusIdentifier) {
- RADIUS radiusPayload;
+ Byte identifier, String sessionId) {
+ // get identifier for request and store mapping to session ID
+ RequestIdentifier radiusIdentifier = idManager.getNewIdentifier(sessionId);
+ if (radiusIdentifier == null) {
+ log.warn("Cannot get identifier supplicant at dev/port: {}/{} " +
+ "with MacAddress {}, dropping packet",
+ deviceId, portNumber, srcMac);
+ return;
+ }
+
log.debug("EAP packet: EAPOL_PACKET ATTR_MD5 from dev/port: {}/{} with MacAddress {}" +
" and Identifier {}", deviceId, portNumber, srcMac, eapPacket.getIdentifier() & 0xff);
// verify if the EAP identifier corresponds to the
@@ -978,28 +1106,32 @@
stateMachine.setLastPacketReceivedTime(System.currentTimeMillis());
if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
//send the RADIUS challenge response
- radiusPayload = getRadiusPayload(stateMachine,
- radiusIdentifier.identifier(), eapPacket);
- radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
+ if (forgeEapolPackets) {
+ handleForgedEapolSuccess(stateMachine);
+ } else {
+ RADIUS radiusPayload = getRadiusPayload(stateMachine,
+ radiusIdentifier.identifier(), eapPacket);
+ radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
- if (stateMachine.challengeState() != null) {
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
- stateMachine.challengeState());
+ if (stateMachine.challengeState() != null) {
+ radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+ stateMachine.challengeState());
+ }
+ radiusPayload.addMessageAuthenticator(radiusSecret);
+ if (outPacketSupp.contains(eapPacket.getIdentifier())) {
+ aaaStatisticsManager.getAaaStats().decrementPendingReqSupp();
+ outPacketSupp.remove(identifier);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Sending ATTR_MD5 packet to RADIUS for supplicant at dev/port: {}/{}" +
+ " with MacAddress {} and Identifier {}", deviceId, portNumber, srcMac,
+ radiusIdentifier.identifier() & 0xff);
+ }
+ sendRadiusPacket(radiusPayload, inPacket);
+ stateMachine.setWaitingForRadiusResponse(true);
+ aaaStatisticsManager.getAaaStats().incrementRadiusReqChallengeTx();
+ aaaStatisticsManager.getAaaStats().incrementEapolMd5RspChall();
}
- radiusPayload.addMessageAuthenticator(radiusSecret);
- if (outPacketSupp.contains(eapPacket.getIdentifier())) {
- aaaStatisticsManager.getAaaStats().decrementPendingReqSupp();
- outPacketSupp.remove(identifier);
- }
- if (log.isTraceEnabled()) {
- log.trace("Sending ATTR_MD5 packet to RADIUS for supplicant at dev/port: {}/{}" +
- " with MacAddress {} and Identifier {}", deviceId, portNumber, srcMac,
- radiusIdentifier.identifier() & 0xff);
- }
- sendRadiusPacket(radiusPayload, inPacket);
- stateMachine.setWaitingForRadiusResponse(true);
- aaaStatisticsManager.getAaaStats().incrementRadiusReqChallengeTx();
- aaaStatisticsManager.getAaaStats().incrementEapolMd5RspChall();
} else {
log.error("eapolIdentifier {} and stateMachine Identifier {} do not " +
"correspond for packet from dev/port: {}/{} with MacAddress {}",
@@ -1017,12 +1149,19 @@
private void handleTls(InboundPacket inPacket, MacAddress srcMac, DeviceId deviceId,
PortNumber portNumber, StateMachine stateMachine, EAP eapPacket,
- Byte identifier, RequestIdentifier radiusIdentifier) {
- RADIUS radiusPayload;
+ Byte identifier, String sessionId) {
+ // get identifier for request and store mapping to session ID
+ RequestIdentifier radiusIdentifier = idManager.getNewIdentifier(sessionId);
+
+ if (radiusIdentifier == null) {
+ log.warn("Cannot get identifier supplicant at dev/port: {}/{} " +
+ "with MacAddress {}, dropping packet", deviceId, portNumber, srcMac);
+ return;
+ }
log.debug("EAP packet: EAPOL_PACKET ATTR_TLS from dev/port: {}/{} with MacAddress {} " +
"and Identifier {}", deviceId, portNumber, srcMac, eapPacket.getIdentifier() & 0xff);
// request id access to RADIUS
- radiusPayload = getRadiusPayload(stateMachine, radiusIdentifier.identifier(), eapPacket);
+ RADIUS radiusPayload = getRadiusPayload(stateMachine, radiusIdentifier.identifier(), eapPacket);
radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
if (stateMachine.challengeState() != null) {
radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
diff --git a/app/src/main/java/org/opencord/aaa/impl/EapolPacketGenerator.java b/app/src/main/java/org/opencord/aaa/impl/EapolPacketGenerator.java
new file mode 100644
index 0000000..5a64bf0
--- /dev/null
+++ b/app/src/main/java/org/opencord/aaa/impl/EapolPacketGenerator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.impl;
+
+import org.onlab.packet.EAP;
+
+/**
+ * Class that is responsible to generate fake EAPOL packets in case that
+ * FORGE_EAPOL_PACKETS is set to true in the config.
+ */
+public final class EapolPacketGenerator {
+
+ private EapolPacketGenerator() {}
+
+ public static EAP forgeEapolChallengeAuth() {
+ EAP eapPayload = new EAP(
+ new Integer(1).byteValue(), new Integer(1).byteValue(),
+ new Integer(4).byteValue(),
+ hexStringToByteArray("108b4eb55f859c501b3e14a594c4997bed"));
+ return eapPayload;
+ }
+
+ public static EAP forgeEapolSuccess() {
+ EAP eapPayload = new EAP(
+ new Integer(3).byteValue(),
+ new Integer(2).byteValue(),
+ new Integer(0).byteValue(),
+ hexStringToByteArray("")
+ );
+ return eapPayload;
+ }
+
+ public static byte[] hexStringToByteArray(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+}
diff --git a/app/src/main/java/org/opencord/aaa/impl/IdentifierManager.java b/app/src/main/java/org/opencord/aaa/impl/IdentifierManager.java
index 27f824f..6b4bffb 100644
--- a/app/src/main/java/org/opencord/aaa/impl/IdentifierManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/IdentifierManager.java
@@ -16,22 +16,43 @@
package org.opencord.aaa.impl;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages allocating request identifiers and mapping them to sessions.
*/
public class IdentifierManager {
+ private final Logger log = getLogger(getClass());
+
private static final int MAX_IDENTIFIER = 256;
private BlockingQueue<Integer> freeIdNumbers;
- private ConcurrentMap<RequestIdentifier, String> idToSession;
+ private ConcurrentMap<RequestIdentifier, Pair<String, Long>> idToSession;
+
+ ScheduledFuture<?> scheduledidentifierPruner;
+
+ // TODO read from component config
+ protected static int timeout = 10000;
+ protected static int pollTimeout = 2;
+ protected static int pruningInterval = 3;
/**
* Creates and initializes a new identifier manager.
@@ -44,6 +65,13 @@
for (int i = 2; i < MAX_IDENTIFIER; i++) {
freeIdNumbers.add(i);
}
+
+ ScheduledExecutorService identifierPruner = Executors.newSingleThreadScheduledExecutor(
+ groupedThreads("onos/aaa", "idpruner-%d", log));
+
+ scheduledidentifierPruner = identifierPruner.scheduleAtFixedRate(
+ new IdentifierPruner(), 0,
+ pruningInterval, TimeUnit.SECONDS);
}
/**
@@ -52,19 +80,43 @@
* @param sessionId session this identifier is associated with
* @return identifier
*/
- public synchronized RequestIdentifier getNewIdentifier(String sessionId) {
- int idNum;
+ public RequestIdentifier getNewIdentifier(String sessionId) {
+ // Run this part without the lock.
+ Integer idNum;
try {
- idNum = freeIdNumbers.take();
+ idNum = freeIdNumbers.poll(pollTimeout, TimeUnit.SECONDS);
} catch (InterruptedException e) {
+ // Interrupted case
+ if (log.isTraceEnabled()) {
+ log.trace("Interrupted while waiting for an id");
+ }
return null;
}
+ // Timeout let's exit
+ if (idNum == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Timedout there are no available ids");
+ }
+ return null;
+ }
+ // Start of the synchronized zone. Real contention happens here.
+ // This thread wants to update the session map. The release thread
+ // wants to update the session map first and then free the id. Same
+ // for the pruner. If this thread is interrupted here is not a big issue
+ // its update is not yet visible for the remaining threads: i) the
+ // release thread cannot release an id not yet taken; ii) the pruning
+ // thread cannot prune for the same reason.
+ synchronized (this) {
+ if (log.isTraceEnabled()) {
+ log.trace("Got identifier {} for session {}", idNum, sessionId);
+ }
- RequestIdentifier id = RequestIdentifier.of((byte) idNum);
+ RequestIdentifier id = RequestIdentifier.of((byte) idNum.intValue());
- idToSession.put(id, sessionId);
+ idToSession.put(id, Pair.of(sessionId, System.currentTimeMillis()));
- return id;
+ return id;
+ }
}
/**
@@ -73,8 +125,9 @@
* @param id request ID
* @return session ID
*/
- public String getSessionId(RequestIdentifier id) {
- return idToSession.get(id);
+ public synchronized String getSessionId(RequestIdentifier id) {
+ //TODO this has multiple accesses
+ return idToSession.get(id) == null ? null : idToSession.get(id).getKey();
}
/**
@@ -83,13 +136,76 @@
* @param id request identifier to release
*/
public synchronized void releaseIdentifier(RequestIdentifier id) {
- String session = idToSession.remove(id);
+ if (log.isTraceEnabled()) {
+ log.trace("Releasing identifier {}", id.identifier() & 0xff);
+ }
+
+ Pair<String, Long> session = idToSession.remove(id);
if (session == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Unable to released identifier {} for session null", id.identifier() & 0xff);
+ }
// this id wasn't mapped to a session so is still free
return;
}
// add id number back to set of free ids
- freeIdNumbers.add((int) id.identifier());
+ freeIdNumbers.add(id.identifier() & 0xff);
+
+ if (log.isTraceEnabled()) {
+ log.trace("Released identifier {} for session {}", id.identifier() & 0xff, session.getKey());
+ }
+ }
+
+ /**
+ * Returns true if this ID is currently taken.
+ *
+ * @param id request identifier to check
+ * @return boolean
+ */
+ private boolean isIdentifierTaken(Integer id) {
+ return !freeIdNumbers.contains(id);
+ }
+
+ /**
+ * Returns the count of available identifiers in a given moment.
+ *
+ * @return boolean
+ */
+ public int getAvailableIdentifiers() {
+ return freeIdNumbers.size();
+ }
+
+ private synchronized void pruneIfNeeded() {
+ if (log.isTraceEnabled()) {
+ log.trace("Starting pruning cycle");
+ }
+ // Gets an immutable copy of the ids and release the ones that exceed the timeout
+ Map<RequestIdentifier, Pair<String, Long>> idsToCheck = ImmutableMap.copyOf(idToSession);
+ // We should not modify while iterating - this is why we get a copy
+ Iterator<Map.Entry<RequestIdentifier, Pair<String, Long>>> itr = idsToCheck.entrySet().iterator();
+ itr.forEachRemaining((entry) -> {
+ RequestIdentifier id = entry.getKey();
+ Pair<String, Long> info = entry.getValue();
+ long diff = System.currentTimeMillis() - info.getValue();
+ if (diff >= timeout) {
+ if (log.isTraceEnabled()) {
+ log.trace("Identifier {} for session {} has exceeded timeout {}, releasing",
+ id.identifier() & 0xff, info.getKey(), timeout);
+ }
+ releaseIdentifier(id);
+ }
+ });
+ if (log.isTraceEnabled()) {
+ log.trace("End pruning cycle");
+ }
+ }
+
+ private class IdentifierPruner implements Runnable {
+ @Override
+ public void run() {
+ pruneIfNeeded();
+ }
+
}
}
diff --git a/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java b/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
index 71826c0..e6544ad 100644
--- a/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
+++ b/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
@@ -42,4 +42,7 @@
public static final String PACKET_PROCESSOR_THREADS = "packetProcessorThreads";
public static final int PACKET_PROCESSOR_THREADS_DEFAULT = 10;
+
+ public static final String FORGE_EAPOL_PACKETS = "forgeEapolPackets";
+ public static final boolean FORGE_EAPOL_PACKETS_DEFAULT = false;
}
diff --git a/app/src/main/java/org/opencord/aaa/impl/RequestIdentifier.java b/app/src/main/java/org/opencord/aaa/impl/RequestIdentifier.java
index 9869af1..564c45a 100644
--- a/app/src/main/java/org/opencord/aaa/impl/RequestIdentifier.java
+++ b/app/src/main/java/org/opencord/aaa/impl/RequestIdentifier.java
@@ -18,6 +18,8 @@
import java.util.Objects;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* An identifier for an authentication request.
*/
@@ -30,7 +32,7 @@
*
* @param identifier id number
*/
- private RequestIdentifier(byte identifier) {
+ public RequestIdentifier(byte identifier) {
this.identifier = identifier;
}
@@ -53,6 +55,7 @@
return new RequestIdentifier(identifier);
}
+ @Override
public boolean equals(Object other) {
if (!(other instanceof RequestIdentifier)) {
return false;
@@ -63,7 +66,15 @@
return identifier == that.identifier;
}
+ @Override
public int hashCode() {
return Objects.hashCode(identifier);
}
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("identifier", Byte.toString(identifier))
+ .toString();
+ }
}
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 0414cac..792b7e4 100755
--- a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
@@ -15,7 +15,6 @@
*/
package org.opencord.aaa.impl;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.EthType;
import org.onlab.packet.Ethernet;
@@ -35,10 +34,12 @@
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.packet.PacketPriority.CONTROL;
import static org.slf4j.LoggerFactory.getLogger;
@@ -70,6 +71,12 @@
// Executor for RADIUS communication thread
private ExecutorService executor;
+ // Worker thread for RADIUS communication
+ private ExecutorService worker;
+
+ // To track the received packets
+ int packetNumber = 1;
+
AaaManager aaaManager;
SocketBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
@@ -97,10 +104,9 @@
log.info("Remote RADIUS Server: {}:{}", radiusIpAddress, radiusServerPort);
- executor = Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setNameFormat("AAA-radius-%d").build());
+ executor = newSingleThreadExecutor(groupedThreads("onos/aaa", "radius-%d", log));
executor.execute(radiusListener);
+ worker = newSingleThreadExecutor(groupedThreads("onos/aaa", "radius-packet-%d", log));
}
@Override
@@ -108,6 +114,7 @@
log.info("Closing RADIUS socket: {}:{}", radiusIpAddress, radiusServerPort);
radiusSocket.close();
executor.shutdownNow();
+ worker.shutdownNow();
}
@Override
@@ -155,10 +162,12 @@
aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
}
} catch (IOException e) {
- log.warn("Cannot send packet to RADIUS server", e);
+ log.error("Cannot send packet to RADIUS server", e);
}
}
+ // in the socket base case we don't care about packets coming from the server as nothing meaningful will be
+ // received from the southbound
@Override
public void handlePacketFromServer(PacketContext context) {
InboundPacket pkt = context.inPacket();
@@ -170,43 +179,50 @@
}
}
+ // Handle radius packet for further processing
+ private void handleRadiusPacketInternal(DatagramPacket inboundBasePacket) {
+ RADIUS inboundRadiusPacket;
+ aaaManager.checkForPacketFromUnknownServer(inboundBasePacket.getAddress().getHostAddress());
+ log.debug("Packet #{} received", packetNumber++);
+ try {
+ inboundRadiusPacket = RADIUS.deserializer().deserialize(inboundBasePacket.getData(),
+ 0, inboundBasePacket.getLength());
+ if (log.isTraceEnabled()) {
+ log.trace("Handling inboundRadiusPacket {} with identifier {}", inboundRadiusPacket,
+ inboundRadiusPacket.getIdentifier() & 0xff);
+ }
+ aaaManager.aaaStatisticsManager.handleRoundtripTime(inboundRadiusPacket.getIdentifier());
+ aaaManager.handleRadiusPacket(inboundRadiusPacket);
+ } catch (DeserializationException dex) {
+ aaaManager.aaaStatisticsManager.getAaaStats().increaseMalformedResponsesRx();
+ log.warn("Cannot deserialize packet", dex);
+ }
+ }
+
class RadiusListener implements Runnable {
@Override
public void run() {
boolean done = false;
- int packetNumber = 1;
-
- log.info("UDP listener thread starting up");
- RADIUS inboundRadiusPacket;
+ try {
+ log.info("UDP listener thread starting up, socket buffer size {}",
+ radiusSocket.getReceiveBufferSize());
+ } catch (SocketException e) {
+ log.error("Socket exception", e);
+ }
while (!done) {
try {
byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH];
- DatagramPacket inboundBasePacket =
- new DatagramPacket(packetBuffer, packetBuffer.length);
+ DatagramPacket inboundBasePacket = new DatagramPacket(packetBuffer, packetBuffer.length);
DatagramSocket socket = radiusSocket;
socket.receive(inboundBasePacket);
- aaaManager.checkForPacketFromUnknownServer(inboundBasePacket.getAddress().getHostAddress());
- log.debug("Packet #{} received", packetNumber++);
- try {
- inboundRadiusPacket =
- RADIUS.deserializer()
- .deserialize(inboundBasePacket.getData(),
- 0,
- inboundBasePacket.getLength());
- if (log.isTraceEnabled()) {
- log.trace("Received RADIUS packet with Identifier {}",
- inboundRadiusPacket.getIdentifier() & 0xff);
- }
- aaaManager.aaaStatisticsManager.handleRoundtripTime(inboundRadiusPacket.getIdentifier());
- aaaManager.handleRadiusPacket(inboundRadiusPacket);
- } catch (DeserializationException dex) {
- aaaManager.aaaStatisticsManager.getAaaStats().increaseMalformedResponsesRx();
- log.error("Cannot deserialize packet", dex);
- }
+ worker.execute(() -> handleRadiusPacketInternal(inboundBasePacket));
+
} catch (IOException e) {
log.warn("Socket was closed, exiting listener thread");
done = true;
+ } catch (Exception e) {
+ log.error("RadiusListener thread thrown an exception: {}", e.getMessage(), e);
}
}
log.info("UDP listener thread shutting down");
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 1b5613c..8247dbf 100644
--- a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -738,7 +738,7 @@
this.setSessionTerminateReason(SessionTerminationReasons.TIME_OUT.reason);
delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.TIMEOUT,
- this.supplicantConnectpoint));
+ this.supplicantConnectpoint, toAuthRecord()));
// If StateMachine is not eligible for cleanup yet, reschedule cleanupTimer further.
} else {
this.scheduleTimeout();
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 83d5c42..394f86d 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
@@ -18,6 +18,8 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.onlab.junit.TestUtils;
import org.onlab.packet.BasePacket;
import org.onlab.packet.DeserializationException;
@@ -58,8 +60,15 @@
/**
* Set of tests of the ONOS application component.
*/
+@RunWith(Parameterized.class)
public class AaaManagerTest extends AaaTestBase {
+ // Change this to have more run with mvn
+ @Parameterized.Parameters
+ public static Object[][] data() {
+ return new Object[1][0];
+ }
+
static final String BAD_IP_ADDRESS = "198.51.100.0";
private final Logger log = getLogger(getClass());
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java b/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
index 4ace459..bd68bfb 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
@@ -18,6 +18,8 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.onlab.junit.TestUtils;
import org.onlab.packet.BasePacket;
import org.onlab.packet.DeserializationException;
@@ -62,8 +64,15 @@
/**
* Set of tests of the ONOS application component for AAA Statistics.
*/
+@RunWith(Parameterized.class)
public class AaaStatisticsTest extends AaaTestBase {
+ // Change this to have more run with mvn
+ @Parameterized.Parameters
+ public static Object[][] data() {
+ return new Object[1][0];
+ }
+
static final String BAD_IP_ADDRESS = "198.51.100.0";
static final Long ZERO = (long) 0;
diff --git a/app/src/test/java/org/opencord/aaa/impl/IdentifierManagerTest.java b/app/src/test/java/org/opencord/aaa/impl/IdentifierManagerTest.java
index 890f2c6..afc5ec2 100644
--- a/app/src/test/java/org/opencord/aaa/impl/IdentifierManagerTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/IdentifierManagerTest.java
@@ -19,19 +19,38 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.slf4j.Logger;
-import static org.junit.Assert.assertNotEquals;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
import static org.slf4j.LoggerFactory.getLogger;
+@RunWith(Parameterized.class)
public class IdentifierManagerTest {
+ // Change this to have more run with mvn
+ @Parameterized.Parameters
+ public static Object[][] data() {
+ return new Object[1][0];
+ }
+
IdentifierManager idManager = null;
private final Logger log = getLogger(getClass());
@Before
public void setUp() {
System.out.print("Set up");
+ idManager.timeout = 1500;
+ idManager.pruningInterval = 1;
+ idManager.pollTimeout = 1;
idManager = new IdentifierManager();
}
@@ -54,4 +73,84 @@
idManager.releaseIdentifier(id);
}
}
+
+ @Test(timeout = 3800)
+ public void testIdRelease() {
+ assertEquals(254, idManager.getAvailableIdentifiers());
+ for (int i = 0; i <= 253; i++) {
+ idManager.getNewIdentifier(Integer.toString(i));
+ }
+
+ assertEquals(0, idManager.getAvailableIdentifiers());
+
+ try {
+ TimeUnit.MILLISECONDS.sleep(3500);
+ } catch (InterruptedException e) {
+ log.error("Can't sleep");
+ }
+
+ // check that the queue has been emptied after the timeout occurred
+ assertEquals(254, idManager.getAvailableIdentifiers());
+
+ // check that I can get a new ID immediately (note the timeout in the test declaration)
+ idManager.getNewIdentifier(Integer.toString(254));
+ }
+
+ @Test(timeout = 5000)
+ public void unavailableIds() {
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ Callable<Object> acquireId = () -> idManager.getNewIdentifier(Integer.toString(2));
+
+ // fill the queue
+ for (int i = 2; i <= 255; i++) {
+ idManager.getNewIdentifier(Integer.toString(i));
+ }
+
+ // try to acquire an id
+ Future<Object> futureAcquire = executor.submit(acquireId);
+
+ // wait for the threads to complete
+ RequestIdentifier id = null;
+ try {
+ id = (RequestIdentifier) futureAcquire.get();
+
+ // if we can't get the ID within 2
+ // seconds we'll drop the packet and we'll retry
+ assertNull(id);
+ } catch (InterruptedException | ExecutionException ex) {
+ log.error("Something failed");
+ assertNull(id);
+ }
+ }
+
+ @Test(timeout = 5000)
+ public void availableIds() {
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ Callable<Object> acquireId = () -> idManager.getNewIdentifier(Integer.toString(2));
+
+ // fill the queue
+ for (int i = 2; i <= 255; i++) {
+ idManager.getNewIdentifier(Integer.toString(i));
+ }
+
+ // try to release an id
+ final RequestIdentifier id = new RequestIdentifier((byte) 2);
+ executor.submit(() -> idManager.releaseIdentifier(id));
+ // try to acquire an id
+ Future<Object> futureAcquire = executor.submit(acquireId);
+
+ // wait for the threads to complete
+ RequestIdentifier idGet = null;
+ try {
+ idGet = (RequestIdentifier) futureAcquire.get();
+ assertNotNull(idGet);
+ } catch (InterruptedException | ExecutionException ex) {
+ log.error("Something failed");
+ assertNull(idGet);
+ }
+ }
}
diff --git a/app/src/test/java/org/opencord/aaa/impl/StateMachineTest.java b/app/src/test/java/org/opencord/aaa/impl/StateMachineTest.java
index e66a5f3..7415e2b 100644
--- a/app/src/test/java/org/opencord/aaa/impl/StateMachineTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/StateMachineTest.java
@@ -19,12 +19,22 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import java.util.concurrent.Executors;
import static org.junit.Assert.assertEquals;
+@RunWith(Parameterized.class)
public class StateMachineTest {
+
+ // Change this to have more run with mvn
+ @Parameterized.Parameters
+ public static Object[][] data() {
+ return new Object[1][0];
+ }
+
StateMachine stateMachine = null;
@Before