diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
commit | 6a07d2d622eaa06953f3353e39c080984076e8de (patch) | |
tree | bfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/apps/aaa | |
parent | e6d71622143ff9b2421a1abbe8434b954b5b1099 (diff) |
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/apps/aaa')
8 files changed, 1323 insertions, 540 deletions
diff --git a/framework/src/onos/apps/aaa/pom.xml b/framework/src/onos/apps/aaa/pom.xml index 78fd4a62..b03930a9 100644 --- a/framework/src/onos/apps/aaa/pom.xml +++ b/framework/src/onos/apps/aaa/pom.xml @@ -44,36 +44,41 @@ <dependency> <groupId>org.onosproject</groupId> - <artifactId>onlab-junit</artifactId> - <scope>test</scope> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>org.onosproject</groupId> - <artifactId>onos-api</artifactId> + <artifactId>onos-app-xos-integration</artifactId> <version>${project.version}</version> </dependency> - <dependency> + <dependency> <groupId>org.onosproject</groupId> - <artifactId>onlab-osgi</artifactId> - <version>${project.version}</version> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.felix</groupId> - <artifactId>org.apache.felix.scr.annotations</artifactId> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> <dependency> <groupId>org.onosproject</groupId> - <artifactId>onos-app-xos-integration</artifactId> + <artifactId>onos-api</artifactId> <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> </dependencies> - <build> + <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java index 7e3de885..479ec7ed 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java @@ -15,13 +15,14 @@ */ package org.onosproject.aaa; -import com.google.common.base.Strings; -import com.google.common.collect.Maps; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.Set; + 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.Modified; -import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.DeserializationException; @@ -38,14 +39,16 @@ import org.onlab.packet.RADIUSAttribute; import org.onlab.packet.TpPort; import org.onlab.packet.UDP; import org.onlab.packet.VlanId; -import org.onlab.util.Tools; -import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; import org.onosproject.net.PortNumber; +import org.onosproject.net.config.ConfigFactory; +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.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; @@ -58,28 +61,21 @@ import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.xosintegration.VoltTenantService; -import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Dictionary; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - +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; - /** * AAA application for ONOS. */ @Component(immediate = true) public class AAA { + + // for verbose output + private final Logger log = getLogger(getClass()); + // a list of our dependencies : // to register with ONOS as an application - described next @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @@ -96,8 +92,28 @@ public class AAA { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VoltTenantService voltTenantService; - // for verbose output - private final Logger log = getLogger(getClass()); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry netCfgService; + + // Parsed RADIUS server addresses + protected InetAddress radiusIpAddress; + protected String radiusMacAddress; + + // NAS IP address + protected InetAddress nasIpAddress; + protected String nasMacAddress; + + // RADIUS server secret + protected String radiusSecret; + + // ID of RADIUS switch + protected String radiusSwitch; + + // RADIUS port number + protected long radiusPort; + + // RADIUS server TCP port number + protected short radiusServerPort; // our application-specific event handler private ReactivePacketProcessor processor = new ReactivePacketProcessor(); @@ -105,124 +121,80 @@ public class AAA { // our unique identifier private ApplicationId appId; - // Map of state machines. Each state machine is represented by an - // unique identifier on the switch: dpid + port number - Map stateMachineMap = null; - - // RADIUS server IP address - private static final String DEFAULT_RADIUS_IP = "192.168.1.10"; - // NAS IP address - private static final String DEFAULT_NAS_IP = "192.168.1.11"; - // RADIUS uplink port - private static final int DEFAULT_RADIUS_UPLINK = 2; - // RADIUS server shared secret - private static final String DEFAULT_RADIUS_SECRET = "ONOSecret"; - // RADIUS MAC address - private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10"; - // NAS MAC address - private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01"; - // Radius Switch Id - private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9"; - // Radius Port Number - private static final String DEFAULT_RADIUS_PORT = "129"; - - @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP, - label = "RADIUS IP Address") - private String radiusIpAddress = DEFAULT_RADIUS_IP; - - @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP, - label = "NAS IP Address") - private String nasIpAddress = DEFAULT_NAS_IP; - - @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS, - label = "RADIUS MAC Address") - private String radiusMacAddress = RADIUS_MAC_ADDRESS; - - @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS, - label = "NAS MAC Address") - private String nasMacAddress = NAS_MAC_ADDRESS; - - @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET, - label = "RADIUS shared secret") - private String radiusSecret = DEFAULT_RADIUS_SECRET; - - @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH, - label = "Radius switch") - private String radiusSwitch = DEFAULT_RADIUS_SWITCH; - - @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT, - label = "Radius port") - private String radiusPort = DEFAULT_RADIUS_PORT; - - // Parsed RADIUS server IP address - protected InetAddress parsedRadiusIpAddress; - - // Parsed NAS IP address - protected InetAddress parsedNasIpAddress; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ComponentConfigService cfgService; - - @Modified - public void modified(ComponentContext context) { - Dictionary<?, ?> properties = context.getProperties(); - - String s = Tools.get(properties, "radiusIpAddress"); - try { - parsedRadiusIpAddress = InetAddress.getByName(s); - radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s; - } catch (UnknownHostException e) { - log.error("Invalid RADIUS IP address specification: {}", s); - } - try { - s = Tools.get(properties, "nasIpAddress"); - parsedNasIpAddress = InetAddress.getByName(s); - nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s; - } catch (UnknownHostException e) { - log.error("Invalid NAS IP address specification: {}", s); - } + // Configuration properties factory + private final ConfigFactory factory = + new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY, + AAAConfig.class, + "AAA") { + @Override + public AAAConfig createConfig() { + return new AAAConfig(); + } + }; - s = Tools.get(properties, "radiusMacAddress"); - radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s; + // Listener for config changes + private final InternalConfigListener cfgListener = new InternalConfigListener(); - s = Tools.get(properties, "nasMacAddress"); - nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s; + /** + * Builds an EAPOL packet based on the given parameters. + * + * @param dstMac destination MAC address + * @param srcMac source MAC address + * @param vlan vlan identifier + * @param eapolType EAPOL type + * @param eap EAP payload + * @return Ethernet frame + */ + private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac, + short vlan, byte eapolType, EAP eap) { - s = Tools.get(properties, "radiusSecret"); - radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s; + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(dstMac.toBytes()); + eth.setSourceMACAddress(srcMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + if (vlan != Ethernet.VLAN_UNTAGGED) { + eth.setVlanID(vlan); + } + //eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(eapolType); + eapol.setPacketLength(eap.getLength()); - s = Tools.get(properties, "radiusSwitchId"); - radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s; + //eap part + eapol.setPayload(eap); - s = Tools.get(properties, "radiusPortNumber"); - radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s; + eth.setPayload(eapol); + eth.setPad(true); + return eth; } @Activate - public void activate(ComponentContext context) { - cfgService.registerProperties(getClass()); - modified(context); + public void activate() { + netCfgService.addListener(cfgListener); + netCfgService.registerConfigFactory(factory); + // "org.onosproject.aaa" is the FQDN of our app appId = coreService.registerApplication("org.onosproject.aaa"); + + cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AAAConfig.class)); + // register our event handler packetService.addProcessor(processor, PacketProcessor.director(2)); requestIntercepts(); - // Instantiate the map of the state machines - stateMachineMap = Collections.synchronizedMap(Maps.newHashMap()); - hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); + StateMachine.initializeMaps(); + hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); } @Deactivate public void deactivate() { - cfgService.unregisterProperties(getClass(), false); - appId = coreService.registerApplication("org.onosproject.aaa"); withdrawIntercepts(); // de-register and null our handler packetService.removeProcessor(processor); processor = null; + StateMachine.destroyMaps(); } /** @@ -237,8 +209,8 @@ public class AAA { TrafficSelector radSelector = DefaultTrafficSelector.builder() .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(1812)) - .matchUdpSrc(TpPort.tpPort(1812)) + .matchUdpDst(TpPort.tpPort(radiusServerPort)) + .matchUdpSrc(TpPort.tpPort(radiusServerPort)) .build(); packetService.requestPackets(radSelector, CONTROL, appId); } @@ -254,45 +226,12 @@ public class AAA { TrafficSelector radSelector = DefaultTrafficSelector.builder() .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(1812)) - .matchUdpSrc(TpPort.tpPort(1812)) + .matchUdpDst(TpPort.tpPort(radiusServerPort)) + .matchUdpSrc(TpPort.tpPort(radiusServerPort)) .build(); packetService.cancelPackets(radSelector, CONTROL, appId); } - /** - * Builds an EAPOL packet based on the given parameters. - * - * @param dstMac destination MAC address - * @param srcMac source MAC address - * @param vlan vlan identifier - * @param eapolType EAPOL type - * @param eap EAP payload - * @return Ethernet frame - */ - private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac, - short vlan, byte eapolType, EAP eap) { - - Ethernet eth = new Ethernet(); - eth.setDestinationMACAddress(dstMac.toBytes()); - eth.setSourceMACAddress(srcMac.toBytes()); - eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); - if (vlan != Ethernet.VLAN_UNTAGGED) { - eth.setVlanID(vlan); - } - //eapol header - EAPOL eapol = new EAPOL(); - eapol.setEapolType(eapolType); - eapol.setPacketLength(eap.getLength()); - - //eap part - eapol.setPayload(eap); - - eth.setPayload(eapol); - eth.setPad(true); - return eth; - } - // our handler defined as a private inner class /** @@ -308,42 +247,66 @@ public class AAA { if (ethPkt == null) { return; } - // identify if incoming packet comes from supplicant (EAP) or RADIUS - switch (EthType.EtherType.lookup(ethPkt.getEtherType())) { - case EAPOL: - handleSupplicantPacket(context.inPacket()); - break; - case IPV4: - IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); - Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress()); - Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress); - if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) { - // TODO: check for port as well when it's configurable - UDP udpPacket = (UDP) ipv4Packet.getPayload(); - - byte[] datagram = udpPacket.getPayload().serialize(); - RADIUS radiusPacket; - try { + try { + // identify if incoming packet comes from supplicant (EAP) or RADIUS + switch (EthType.EtherType.lookup(ethPkt.getEtherType())) { + case EAPOL: + handleSupplicantPacket(context.inPacket()); + break; + case IPV4: + IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); + Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress()); + Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress); + if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) { + // TODO: check for port as well when it's configurable + UDP udpPacket = (UDP) ipv4Packet.getPayload(); + + byte[] datagram = udpPacket.getPayload().serialize(); + RADIUS radiusPacket; radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length); - } catch (DeserializationException e) { - log.warn("Unable to deserialize RADIUS packet:", e); - return; + handleRadiusPacket(radiusPacket); } - handleRadiusPacket(radiusPacket); - } - break; - default: - return; + + break; + default: + log.trace("Skipping Ethernet packet type {}", + EthType.EtherType.lookup(ethPkt.getEtherType())); + } + } catch (DeserializationException | StateMachineException e) { + log.warn("Unable to process RADIUS packet:", e); } } + /** + * Creates and initializes common fields of a RADIUS packet. + * + * @param identifier RADIUS identifier + * @param eapPacket EAP packet + * @return RADIUS packet + */ + private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) { + RADIUS radiusPayload = + new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, + eapPacket.getIdentifier()); + radiusPayload.setIdentifier(identifier); + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, + eapPacket.getData()); + + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, + AAA.this.nasIpAddress.getAddress()); + + radiusPayload.encapsulateMessage(eapPacket); + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + + return radiusPayload; + } /** * Handles PAE packets (supplicant). * * @param inPacket Ethernet packet coming from the supplicant */ - private void handleSupplicantPacket(InboundPacket inPacket) { + private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException { Ethernet ethPkt = inPacket.parsed(); // Where does it come from? MacAddress srcMAC = ethPkt.getSourceMAC(); @@ -351,114 +314,83 @@ public class AAA { DeviceId deviceId = inPacket.receivedFrom().deviceId(); PortNumber portNumber = inPacket.receivedFrom().port(); String sessionId = deviceId.toString() + portNumber.toString(); - StateMachine stateMachine = getStateMachine(sessionId); + StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId); + if (stateMachine == null) { + stateMachine = new StateMachine(sessionId, voltTenantService); + } + EAPOL eapol = (EAPOL) ethPkt.getPayload(); switch (eapol.getEapolType()) { case EAPOL.EAPOL_START: - try { - stateMachine.start(); - stateMachine.supplicantConnectpoint = inPacket.receivedFrom(); - - //send an EAP Request/Identify to the supplicant - EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null); - Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L), - ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, - eapPayload); - stateMachine.supplicantAddress = srcMAC; - stateMachine.vlanId = ethPkt.getVlanID(); - - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); - } catch (StateMachineException e) { - e.printStackTrace(); - } + stateMachine.start(); + stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom()); + + //send an EAP Request/Identify to the supplicant + EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null); + Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L), + ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, + eapPayload); + stateMachine.setSupplicantAddress(srcMAC); + stateMachine.setVlanId(ethPkt.getVlanID()); + + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case EAPOL.EAPOL_PACKET: - //check if this is a Response/Identify or a Response/TLS + RADIUS radiusPayload; + // check if this is a Response/Identify or a Response/TLS EAP eapPacket = (EAP) eapol.getPayload(); byte dataType = eapPacket.getDataType(); switch (dataType) { + case EAP.ATTR_IDENTITY: - try { - //request id access to RADIUS - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - eapPacket.getData()); - stateMachine.setUsername(eapPacket.getData()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); - - // set Request Authenticator in StateMachine - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); - sendRadiusMessage(radiusPayload); + // request id access to RADIUS + stateMachine.setUsername(eapPacket.getData()); - //change the state to "PENDING" - stateMachine.requestAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); + + // set Request Authenticator in StateMachine + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); + sendRadiusMessage(radiusPayload); + + // change the state to "PENDING" + stateMachine.requestAccess(); break; case EAP.ATTR_MD5: - //verify if the EAP identifier corresponds to the challenge identifier from the client state - //machine. - if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) { + // verify if the EAP identifier corresponds to the + // challenge identifier from the client state + // machine. + if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) { //send the RADIUS challenge response - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - stateMachine.getUsername()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); + radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, - stateMachine.getChallengeState()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + stateMachine.challengeState()); sendRadiusMessage(radiusPayload); } break; case EAP.ATTR_TLS: - try { - //request id access to RADIUS - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - stateMachine.getUsername()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); + // request id access to RADIUS + radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, - stateMachine.getChallengeState()); - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, + stateMachine.challengeState()); + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + sendRadiusMessage(radiusPayload); + // TODO: this gets called on every fragment, should only be called at TLS-Start + stateMachine.requestAccess(); - sendRadiusMessage(radiusPayload); - // TODO: this gets called on every fragment, should only be called at TLS-Start - stateMachine.requestAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } break; default: return; } break; default: - return; + log.trace("Skipping EAPOL message {}", eapol.getEapolType()); } } @@ -467,99 +399,46 @@ public class AAA { * * @param radiusPacket RADIUS packet coming from the RADIUS server. */ - private void handleRadiusPacket(RADIUS radiusPacket) { - StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier()); + private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException { + StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier()); if (stateMachine == null) { log.error("Invalid session identifier, exiting..."); return; } - EAP eapPayload = new EAP(); - Ethernet eth = null; + EAP eapPayload; + Ethernet eth; switch (radiusPacket.getCode()) { case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE: byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue(); eapPayload = radiusPacket.decapsulateMessage(); stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState); - eth = buildEapolResponse(stateMachine.supplicantAddress, - MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, + eth = buildEapolResponse(stateMachine.supplicantAddress(), + MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case RADIUS.RADIUS_CODE_ACCESS_ACCEPT: - try { - //send an EAPOL - Success to the supplicant. - byte[] eapMessage = - radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue(); - eapPayload = new EAP(); - eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); - eth = buildEapolResponse(stateMachine.supplicantAddress, - MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, - eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); - - stateMachine.authorizeAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + //send an EAPOL - Success to the supplicant. + byte[] eapMessage = + radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue(); + eapPayload = new EAP(); + eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); + eth = buildEapolResponse(stateMachine.supplicantAddress(), + MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, + eapPayload); + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); + + stateMachine.authorizeAccess(); break; case RADIUS.RADIUS_CODE_ACCESS_REJECT: - try { - stateMachine.denyAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + stateMachine.denyAccess(); break; default: log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode()); } } - private StateMachine getStateMachineById(byte identifier) { - StateMachine stateMachine = null; - Set stateMachineSet = stateMachineMap.entrySet(); - - synchronized (stateMachineMap) { - Iterator itr = stateMachineSet.iterator(); - while (itr.hasNext()) { - Map.Entry entry = (Map.Entry) itr.next(); - stateMachine = (StateMachine) entry.getValue(); - if (identifier == stateMachine.getIdentifier()) { - //the state machine has already been created for this session session - stateMachine = (StateMachine) entry.getValue(); - break; - } - } - } - - return stateMachine; - } - - private StateMachine getStateMachine(String sessionId) { - StateMachine stateMachine = null; - Set stateMachineSet = stateMachineMap.entrySet(); - - synchronized (stateMachineMap) { - Iterator itr = stateMachineSet.iterator(); - while (itr.hasNext()) { - - Map.Entry entry = (Map.Entry) itr.next(); - if (sessionId.equals(entry.getKey())) { - //the state machine has already been created for this session session - stateMachine = (StateMachine) entry.getValue(); - break; - } - } - } - - if (stateMachine == null) { - stateMachine = new StateMachine(sessionId, voltTenantService); - stateMachineMap.put(sessionId, stateMachine); - } - - return stateMachine; - } - private void sendRadiusMessage(RADIUS radiusMessage) { Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress)); Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst(); @@ -576,12 +455,12 @@ public class AAA { IPv4 ip4Packet = new IPv4(); Ethernet ethPkt = new Ethernet(); radiusMessage.setParent(udp); - udp.setDestinationPort((short) 1812); - udp.setSourcePort((short) 1812); // TODO: make this configurable + udp.setDestinationPort(radiusServerPort); + udp.setSourcePort(radiusServerPort); udp.setPayload(radiusMessage); udp.setParent(ip4Packet); - ip4Packet.setSourceAddress(AAA.this.nasIpAddress); - ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress); + ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress()); + ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress()); ip4Packet.setProtocol(IPv4.PROTOCOL_UDP); ip4Packet.setPayload(udp); ip4Packet.setParent(ethPkt); @@ -591,7 +470,7 @@ public class AAA { ethPkt.setPayload(ip4Packet); TrafficTreatment treatment = DefaultTrafficTreatment.builder() - .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build(); + .setOutput(PortNumber.portNumber(radiusPort)).build(); OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch), treatment, ByteBuffer.wrap(ethPkt.serialize())); packetService.emit(packet); @@ -613,4 +492,59 @@ public class AAA { } + private class InternalConfigListener implements NetworkConfigListener { + + /** + * Reconfigures the DHCP Server according to the configuration parameters passed. + * + * @param cfg configuration object + */ + private void reconfigureNetwork(AAAConfig cfg) { + AAAConfig newCfg; + if (cfg == null) { + newCfg = new AAAConfig(); + } else { + newCfg = cfg; + } + 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(); + } + 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(); + } + } + + @Override + public void event(NetworkConfigEvent event) { + + if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) && + event.configClass().equals(AAAConfig.class)) { + + AAAConfig cfg = netCfgService.getConfig(appId, AAAConfig.class); + reconfigureNetwork(cfg); + log.info("Reconfigured"); + } + } + } + + } diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java new file mode 100644 index 00000000..c18d2bf2 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java @@ -0,0 +1,239 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.aaa; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.onosproject.core.ApplicationId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.basics.BasicElementConfig; + +/** + * Network config for the AAA app. + */ +public class AAAConfig extends Config<ApplicationId> { + + private static final String RADIUS_IP = "radiusIp"; + private static final String RADIUS_SERVER_PORT = "1812"; + private static final String RADIUS_MAC = "radiusMac"; + 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"; + + // RADIUS server IP address + protected static final String DEFAULT_RADIUS_IP = "192.168.1.10"; + + // RADIUS MAC address + protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10"; + + // NAS IP address + protected static final String DEFAULT_NAS_IP = "192.168.1.11"; + + // NAS MAC address + protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01"; + + // 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 = "129"; + + // Radius Server UDP Port Number + protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812"; + + /** + * Gets the value of a string property, protecting for an empty + * JSON object. + * + * @param name name of the property + * @param defaultValue default value if none has been specified + * @return String value if one os found, default value otherwise + */ + private String getStringProperty(String name, String defaultValue) { + if (object == null) { + return defaultValue; + } + return get(name, defaultValue); + } + + /** + * Returns the NAS ip. + * + * @return ip address or null if not set + */ + public InetAddress nasIp() { + try { + return InetAddress.getByName(getStringProperty(NAS_IP, DEFAULT_NAS_IP)); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Sets the NAS ip. + * + * @param ip new ip address; null to clear + * @return self + */ + public BasicElementConfig nasIp(String ip) { + return (BasicElementConfig) setOrClear(NAS_IP, ip); + } + + /** + * Returns the RADIUS server ip. + * + * @return ip address or null if not set + */ + public InetAddress radiusIp() { + try { + return InetAddress.getByName(getStringProperty(RADIUS_IP, DEFAULT_RADIUS_IP)); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Sets the RADIUS server ip. + * + * @param ip new ip address; null to clear + * @return self + */ + public BasicElementConfig radiusIp(String ip) { + return (BasicElementConfig) setOrClear(RADIUS_IP, ip); + } + + /** + * Returns the RADIUS MAC address. + * + * @return mac address or null if not set + */ + public String radiusMac() { + return getStringProperty(RADIUS_MAC, DEFAULT_RADIUS_MAC); + } + + /** + * Sets the RADIUS MAC address. + * + * @param mac new MAC address; null to clear + * @return self + */ + public BasicElementConfig radiusMac(String mac) { + return (BasicElementConfig) setOrClear(RADIUS_MAC, mac); + } + + /** + * Returns the RADIUS MAC address. + * + * @return mac address or null if not set + */ + public String nasMac() { + return getStringProperty(NAS_MAC, DEFAULT_NAS_MAC); + } + + /** + * Sets the RADIUS MAC address. + * + * @param mac new MAC address; null to clear + * @return self + */ + public BasicElementConfig nasMac(String mac) { + return (BasicElementConfig) setOrClear(NAS_MAC, mac); + } + + /** + * Returns the RADIUS secret. + * + * @return radius secret or null if not set + */ + public String radiusSecret() { + return getStringProperty(RADIUS_SECRET, DEFAULT_RADIUS_SECRET); + } + + /** + * Sets the RADIUS secret. + * + * @param secret new MAC address; null to clear + * @return self + */ + public BasicElementConfig radiusSecret(String secret) { + return (BasicElementConfig) setOrClear(RADIUS_SECRET, secret); + } + + /** + * 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. + */ + public short radiusServerUDPPort() { + return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT, + DEFAULT_RADIUS_SERVER_PORT)); + } + + /** + * Sets the RADIUS port. + * + * @param port new RADIUS UDP port; -1 to clear + * @return self + */ + public BasicElementConfig radiusServerUDPPort(short port) { + return (BasicElementConfig) setOrClear(RADIUS_SERVER_PORT, (long) port); + } + +} diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java index 60959ada..84f69241 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java @@ -18,13 +18,16 @@ package org.onosproject.aaa; +import java.util.BitSet; +import java.util.Map; + import org.onlab.packet.MacAddress; import org.onosproject.net.ConnectPoint; import org.onosproject.xosintegration.VoltTenant; import org.onosproject.xosintegration.VoltTenantService; import org.slf4j.Logger; -import java.util.BitSet; +import com.google.common.collect.Maps; import static org.slf4j.LoggerFactory.getLogger; @@ -58,9 +61,9 @@ class StateMachine { private byte[] requestAuthenticator; // Supplicant connectivity info - protected ConnectPoint supplicantConnectpoint; - protected MacAddress supplicantAddress; - protected short vlanId; + private ConnectPoint supplicantConnectpoint; + private MacAddress supplicantAddress; + private short vlanId; private String sessionId = null; @@ -109,8 +112,28 @@ class StateMachine { private int currentState = STATE_IDLE; + // Maps of state machines. Each state machine is represented by an + // unique identifier on the switch: dpid + port number + private static Map<String, StateMachine> sessionIdMap; + private static Map<Integer, StateMachine> identifierMap; - /** + public static void initializeMaps() { + sessionIdMap = Maps.newConcurrentMap(); + identifierMap = Maps.newConcurrentMap(); + } + + public static void destroyMaps() { + sessionIdMap = null; + identifierMap = null; + } + + public static StateMachine lookupStateMachineById(byte identifier) { + return identifierMap.get((int) identifier); + } + + public static StateMachine lookupStateMachineBySessionId(String sessionId) { + return sessionIdMap.get(sessionId); + } /** * State Machine Constructor. * * @param sessionId session Id represented by the switch dpid + port number @@ -120,15 +143,69 @@ class StateMachine { log.info("Creating a new state machine for {}", sessionId); this.sessionId = sessionId; this.voltService = voltService; + sessionIdMap.put(sessionId, this); + } + + /** + * Gets the connect point for the supplicant side. + * + * @return supplicant connect point + */ + public ConnectPoint supplicantConnectpoint() { + return supplicantConnectpoint; + } + + /** + * Sets the supplicant side connect point. + * + * @param supplicantConnectpoint supplicant select point. + */ + public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) { + this.supplicantConnectpoint = supplicantConnectpoint; + } + + /** + * Gets the MAC address of the supplicant. + * + * @return supplicant MAC address + */ + public MacAddress supplicantAddress() { + return supplicantAddress; + } + + /** + * Sets the supplicant MAC address. + * + * @param supplicantAddress new supplicant MAC address + */ + public void setSupplicantAddress(MacAddress supplicantAddress) { + this.supplicantAddress = supplicantAddress; + } + + /** + * Gets the client's Vlan ID. + * + * @return client vlan ID + */ + public short vlanId() { + return vlanId; + } + /** + * Sets the client's vlan ID. + * + * @param vlanId new client vlan ID + */ + public void setVlanId(short vlanId) { + this.vlanId = vlanId; } /** - * Get the client id that is requesting for access. + * Gets the client id that is requesting for access. * * @return The client id. */ - public String getSessionId() { + public String sessionId() { return this.sessionId; } @@ -137,7 +214,7 @@ class StateMachine { */ private void createIdentifier() throws StateMachineException { log.debug("Creating Identifier."); - int index = -1; + int index; try { //find the first available spot for identifier assignment @@ -178,11 +255,11 @@ class StateMachine { } /** - * Get the challenge EAP identifier set by the RADIUS. + * Gets the challenge EAP identifier set by the RADIUS. * * @return The challenge EAP identifier. */ - protected byte getChallengeIdentifier() { + protected byte challengeIdentifier() { return this.challengeIdentifier; } @@ -198,11 +275,11 @@ class StateMachine { } /** - * Get the challenge state set by the RADIUS. + * Gets the challenge state set by the RADIUS. * * @return The challenge state. */ - protected byte[] getChallengeState() { + protected byte[] challengeState() { return this.challengeState; } @@ -217,16 +294,16 @@ class StateMachine { /** - * Get the username. + * Gets the username. * * @return The requestAuthenticator. */ - protected byte[] getReqeustAuthenticator() { + protected byte[] requestAuthenticator() { return this.requestAuthenticator; } /** - * Set the username. + * Sets the authenticator. * * @param authenticator The username sent to the RADIUS upon access request. */ @@ -236,11 +313,11 @@ class StateMachine { /** - * Get the username. + * Gets the username. * * @return The username. */ - protected byte[] getUsername() { + protected byte[] username() { return this.username; } @@ -249,7 +326,7 @@ class StateMachine { * * @return The state machine identifier. */ - public byte getIdentifier() { + public byte identifier() { return (byte) this.identifier; } @@ -267,7 +344,7 @@ class StateMachine { /** * Move to the next state. * - * @param msg + * @param msg message */ private void next(int msg) { currentState = transition[currentState][msg]; @@ -280,14 +357,11 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void start() throws StateMachineException { - try { - states[currentState].start(); - //move to the next state - next(TRANSITION_START); - createIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].start(); + //move to the next state + next(TRANSITION_START); + createIdentifier(); + identifierMap.put(identifier, this); } /** @@ -297,13 +371,9 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void requestAccess() throws StateMachineException { - try { - states[currentState].requestAccess(); - //move to the next state - next(TRANSITION_REQUEST_ACCESS); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].requestAccess(); + //move to the next state + next(TRANSITION_REQUEST_ACCESS); } /** @@ -313,27 +383,22 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void authorizeAccess() throws StateMachineException { - try { - states[currentState].radiusAccepted(); - //move to the next state - next(TRANSITION_AUTHORIZE_ACCESS); - - if (voltService != null) { - voltService.addTenant( - VoltTenant.builder() - .withHumanReadableName("VCPE-" + this.identifier) - .withId(this.identifier) - .withProviderService(1) - .withServiceSpecificId(String.valueOf(this.identifier)) - .withPort(this.supplicantConnectpoint) - .withVlanId(String.valueOf(this.vlanId)).build()); - } - - deleteIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); + states[currentState].radiusAccepted(); + //move to the next state + next(TRANSITION_AUTHORIZE_ACCESS); + + if (voltService != null) { + voltService.addTenant( + VoltTenant.builder() + .withHumanReadableName("VCPE-" + this.identifier) + .withId(this.identifier) + .withProviderService(1) + .withServiceSpecificId(String.valueOf(this.identifier)) + .withPort(this.supplicantConnectpoint) + .withVlanId(String.valueOf(this.vlanId)).build()); } + deleteIdentifier(); } /** @@ -343,14 +408,10 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void denyAccess() throws StateMachineException { - try { - states[currentState].radiusDenied(); - //move to the next state - next(TRANSITION_DENY_ACCESS); - deleteIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].radiusDenied(); + //move to the next state + next(TRANSITION_DENY_ACCESS); + deleteIdentifier(); } /** @@ -360,141 +421,117 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void logoff() throws StateMachineException { - try { - states[currentState].logoff(); - //move to the next state - next(TRANSITION_LOGOFF); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].logoff(); + //move to the next state + next(TRANSITION_LOGOFF); } /** - * Get the current state. + * Gets the current state. * * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED, * STATE_UNAUTHORIZED. */ - public int getState() { + public int state() { return currentState; } - + @Override public String toString() { return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" + ("state: " + this.currentState); } -} -// FIXME: A source file should contain no more than one top-level entity! + abstract class State { + private final Logger log = getLogger(getClass()); -abstract class State { - private final Logger log = getLogger(getClass()); - - private String name = "State"; + private String name = "State"; - public void start() throws StateMachineInvalidTransitionException { - log.warn("START transition from this state is not allowed."); - } + public void start() throws StateMachineInvalidTransitionException { + log.warn("START transition from this state is not allowed."); + } - public void requestAccess() throws StateMachineInvalidTransitionException { - log.warn("REQUEST ACCESS transition from this state is not allowed."); - } + public void requestAccess() throws StateMachineInvalidTransitionException { + log.warn("REQUEST ACCESS transition from this state is not allowed."); + } - public void radiusAccepted() throws StateMachineInvalidTransitionException { - log.warn("AUTHORIZE ACCESS transition from this state is not allowed."); - } + public void radiusAccepted() throws StateMachineInvalidTransitionException { + log.warn("AUTHORIZE ACCESS transition from this state is not allowed."); + } - public void radiusDenied() throws StateMachineInvalidTransitionException { - log.warn("DENY ACCESS transition from this state is not allowed."); - } + public void radiusDenied() throws StateMachineInvalidTransitionException { + log.warn("DENY ACCESS transition from this state is not allowed."); + } - public void logoff() throws StateMachineInvalidTransitionException { - log.warn("LOGOFF transition from this state is not allowed."); + public void logoff() throws StateMachineInvalidTransitionException { + log.warn("LOGOFF transition from this state is not allowed."); + } } -} -/** - * Idle state: supplicant is logged of from the network. - */ -class Idle extends State { - private final Logger log = getLogger(getClass()); - private String name = "IDLE_STATE"; + /** + * Idle state: supplicant is logged of from the network. + */ + class Idle extends State { + private final Logger log = getLogger(getClass()); + private String name = "IDLE_STATE"; - public void start() { - log.info("Moving from IDLE state to STARTED state."); + public void start() { + log.info("Moving from IDLE state to STARTED state."); + } } -} -/** - * Started state: supplicant has entered the network and informed the authenticator. - */ -class Started extends State { - private final Logger log = getLogger(getClass()); - private String name = "STARTED_STATE"; + /** + * Started state: supplicant has entered the network and informed the authenticator. + */ + class Started extends State { + private final Logger log = getLogger(getClass()); + private String name = "STARTED_STATE"; - public void requestAccess() { - log.info("Moving from STARTED state to PENDING state."); + public void requestAccess() { + log.info("Moving from STARTED state to PENDING state."); + } } -} -/** - * Pending state: supplicant has been identified by the authenticator but has not access yet. - */ -class Pending extends State { - private final Logger log = getLogger(getClass()); - private String name = "PENDING_STATE"; + /** + * Pending state: supplicant has been identified by the authenticator but has not access yet. + */ + class Pending extends State { + private final Logger log = getLogger(getClass()); + private String name = "PENDING_STATE"; - public void radiusAccepted() { - log.info("Moving from PENDING state to AUTHORIZED state."); - } + public void radiusAccepted() { + log.info("Moving from PENDING state to AUTHORIZED state."); + } - public void radiusDenied() { - log.info("Moving from PENDING state to UNAUTHORIZED state."); + public void radiusDenied() { + log.info("Moving from PENDING state to UNAUTHORIZED state."); + } } -} -/** - * Authorized state: supplicant port has been accepted, access is granted. - */ -class Authorized extends State { - private final Logger log = getLogger(getClass()); - private String name = "AUTHORIZED_STATE"; + /** + * Authorized state: supplicant port has been accepted, access is granted. + */ + class Authorized extends State { + private final Logger log = getLogger(getClass()); + private String name = "AUTHORIZED_STATE"; - public void logoff() { + public void logoff() { - log.info("Moving from AUTHORIZED state to IDLE state."); + log.info("Moving from AUTHORIZED state to IDLE state."); + } } -} -/** - * Unauthorized state: supplicant port has been rejected, access is denied. - */ -class Unauthorized extends State { - private final Logger log = getLogger(getClass()); - private String name = "UNAUTHORIZED_STATE"; + /** + * Unauthorized state: supplicant port has been rejected, access is denied. + */ + class Unauthorized extends State { + private final Logger log = getLogger(getClass()); + private String name = "UNAUTHORIZED_STATE"; - public void logoff() { - log.info("Moving from UNAUTHORIZED state to IDLE state."); + public void logoff() { + log.info("Moving from UNAUTHORIZED state to IDLE state."); + } } -} -/** - * Exception for the State Machine. - */ -class StateMachineException extends Exception { - public StateMachineException(String message) { - super(message); - - } -} - -/** - * Exception raised when the transition from one state to another is invalid. - */ -class StateMachineInvalidTransitionException extends StateMachineException { - public StateMachineInvalidTransitionException(String message) { - super(message); - } } diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java new file mode 100644 index 00000000..d4a4da77 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 AT&T Foundry + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.onosproject.aaa; + +/** + * Exception for the State Machine. + */ +class StateMachineException extends Exception { + public StateMachineException(String message) { + super(message); + + } +} diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java new file mode 100644 index 00000000..9f41a34f --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2015 AT&T Foundry + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.onosproject.aaa; + +/** + * Exception raised when the transition from one state to another is invalid. + */ +class StateMachineInvalidTransitionException extends StateMachineException { + public StateMachineInvalidTransitionException(String message) { + super(message); + } +} diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java index 1b581ab1..75a60336 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java @@ -15,29 +15,494 @@ */ package org.onosproject.aaa; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onlab.packet.Data; +import org.onlab.packet.DeserializationException; +import org.onlab.packet.EAP; +import org.onlab.packet.EAPOL; +import org.onlab.packet.EthType; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.RADIUS; +import org.onlab.packet.RADIUSAttribute; +import org.onlab.packet.UDP; +import org.onlab.packet.VlanId; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.net.Annotations; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.NetworkConfigRegistryAdapter; +import org.onosproject.net.host.HostServiceAdapter; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.DefaultPacketContext; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.ProviderId; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.onosproject.net.NetTestTools.connectPoint; /** * Set of tests of the ONOS application component. */ public class AAATest { + MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); + MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a"); + + PacketProcessor packetProcessor; private AAA aaa; + List<Ethernet> savedPackets = new LinkedList<>(); + + /** + * Saves the given packet onto the saved packets list. + * + * @param eth packet to save + */ + private void savePacket(Ethernet eth) { + savedPackets.add(eth); + } + + /** + * Keeps a reference to the PacketProcessor and saves the OutboundPackets. + */ + private class MockPacketService extends PacketServiceAdapter { + + @Override + public void addProcessor(PacketProcessor processor, int priority) { + packetProcessor = processor; + } + + @Override + public void emit(OutboundPacket packet) { + try { + Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(), + 0, packet.data().array().length); + savePacket(eth); + } catch (Exception e) { + fail(e.getMessage()); + } + } + } + + /** + * Mocks the DefaultPacketContext. + */ + private final class TestPacketContext extends DefaultPacketContext { + + private TestPacketContext(long time, InboundPacket inPkt, + OutboundPacket outPkt, boolean block) { + super(time, inPkt, outPkt, block); + } + + @Override + public void send() { + // We don't send anything out. + } + } + + /** + * Mocks a host to allow locating the Radius server. + */ + private static final class MockHost implements Host { + @Override + public HostId id() { + return null; + } + + @Override + public MacAddress mac() { + return null; + } + + @Override + public VlanId vlan() { + return VlanId.vlanId(VlanId.UNTAGGED); + } + + @Override + public Set<IpAddress> ipAddresses() { + return null; + } + + @Override + public HostLocation location() { + return null; + } + + @Override + public Annotations annotations() { + return null; + } + + @Override + public ProviderId providerId() { + return null; + } + } + + /** + * Mocks the Host service. + */ + private static final class MockHostService extends HostServiceAdapter { + @Override + public Set<Host> getHostsByIp(IpAddress ip) { + return ImmutableSet.of(new MockHost()); + } + } + + /** + * Mocks the network config registry. + */ + @SuppressWarnings("unchecked") + private static final class TestNetworkConfigRegistry + extends NetworkConfigRegistryAdapter { + @Override + public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) { + return (C) new AAAConfig(); + } + } + + /** + * Sends an Ethernet packet to the process method of the Packet Processor. + * + * @param reply Ethernet packet + */ + private void sendPacket(Ethernet reply) { + final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize()); + InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1), + reply, + byteBuffer); + + PacketContext context = new TestPacketContext(127L, inPacket, null, false); + packetProcessor.process(context); + } + + /** + * Constructs an Ethernet packet containing a EAPOL_START Payload. + * + * @return Ethernet packet + */ + private Ethernet constructSupplicantStartPacket() { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + eth.setVlanID((short) 2); + + EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null); + + //eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(EAPOL.EAPOL_START); + eapol.setPacketLength(eap.getLength()); + + //eap part + eapol.setPayload(eap); + + eth.setPayload(eapol); + eth.setPad(true); + return eth; + } + + /** + * Constructs an Ethernet packet containing identification payload. + * + * @return Ethernet packet + */ + private Ethernet constructSupplicantIdentifyPacket(byte type) { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + eth.setVlanID((short) 2); + + String username = "user"; + EAP eap = new EAP(EAP.REQUEST, (byte) 1, type, + username.getBytes(Charsets.US_ASCII)); + eap.setIdentifier((byte) 1); + + // eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(EAPOL.EAPOL_PACKET); + eapol.setPacketLength(eap.getLength()); + + // eap part + eapol.setPayload(eap); + + eth.setPayload(eapol); + eth.setPad(true); + return eth; + } + + /** + * Constructs an Ethernet packet containing a RADIUS challenge + * packet. + * + * @param challengeCode code to use in challenge packet + * @param challengeType type to use in challenge packet + * @return Ethernet packet + */ + private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort()); + eth.setVlanID((short) 2); + + IPv4 ipv4 = new IPv4(); + ipv4.setProtocol(IPv4.PROTOCOL_UDP); + ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress()); + + String challenge = "1234"; + + EAP eap = new EAP(challengeType, (byte) 1, challengeType, + challenge.getBytes(Charsets.US_ASCII)); + eap.setIdentifier((byte) 1); + + RADIUS radius = new RADIUS(); + radius.setCode(challengeCode); + + radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, + challenge.getBytes(Charsets.US_ASCII)); + radius.setPayload(eap); + radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE, + eap.serialize()); + + UDP udp = new UDP(); + udp.setPayload(radius); + ipv4.setPayload(udp); + + eth.setPayload(ipv4); + eth.setPad(true); + return eth; + } + + /** + * Sets up the services required by the AAA application. + */ @Before public void setUp() { - + aaa = new AAA(); + aaa.netCfgService = new TestNetworkConfigRegistry(); + aaa.coreService = new CoreServiceAdapter(); + aaa.packetService = new MockPacketService(); + aaa.hostService = new MockHostService(); + aaa.activate(); } + /** + * Tears down the AAA application. + */ @After public void tearDown() { + aaa.deactivate(); + } + + /** + * Extracts the RADIUS packet from a packet sent by the supplicant. + * + * @param supplicantPacket packet sent by the supplicant + * @return RADIUS packet + * @throws DeserializationException if deserialization of the packet contents + * fails. + */ + private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket) + throws DeserializationException { + assertThat(supplicantPacket, notNullValue()); + assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED)); + assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress)); + assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress)); + + assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class)); + IPv4 ipv4 = (IPv4) supplicantPacket.getPayload(); + assertThat(ipv4, notNullValue()); + assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(), + is(aaa.nasIpAddress.getHostAddress())); + assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(), + is(aaa.radiusIpAddress.getHostAddress())); + + assertThat(ipv4.getPayload(), instanceOf(UDP.class)); + UDP udp = (UDP) ipv4.getPayload(); + assertThat(udp, notNullValue()); + + assertThat(udp.getPayload(), instanceOf(Data.class)); + Data data = (Data) udp.getPayload(); + RADIUS radius = RADIUS.deserializer() + .deserialize(data.getData(), 0, data.getData().length); + assertThat(radius, notNullValue()); + return radius; } + /** + * Checks the contents of a RADIUS packet being sent to the RADIUS server. + * + * @param radiusPacket packet to check + * @param code expected code + */ + private void checkRadiusPacket(Ethernet radiusPacket, byte code) { + assertThat(radiusPacket.getVlanID(), is((short) 2)); + + // TODO: These address values seem wrong, but are produced by the current AAA implementation + assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L))); + assertThat(radiusPacket.getDestinationMAC(), is(serverMac)); + + assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class)); + EAPOL eapol = (EAPOL) radiusPacket.getPayload(); + assertThat(eapol, notNullValue()); + + assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET)); + assertThat(eapol.getPayload(), instanceOf(EAP.class)); + EAP eap = (EAP) eapol.getPayload(); + assertThat(eap, notNullValue()); + assertThat(eap.getCode(), is(code)); + } + + /** + * Fetches the sent packet at the given index. The requested packet + * must be the last packet on the list. + * + * @param index index into sent packets array + * @return packet + */ + private Ethernet fetchPacket(int index) { + assertThat(savedPackets.size(), is(index + 1)); + Ethernet eth = savedPackets.get(index); + assertThat(eth, notNullValue()); + return eth; + } + + /** + * Tests the authentication path through the AAA application. + * + * @throws DeserializationException if packed deserialization fails. + */ @Test - public void basics() { + public void testAuthentication() throws DeserializationException { + + // Our session id will be the device ID ("of:1") with the port ("1") concatenated + String sessionId = "of:11"; + + // (1) Supplicant start up + + Ethernet startPacket = constructSupplicantStartPacket(); + sendPacket(startPacket); + + Ethernet responsePacket = fetchPacket(0); + checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY); + + // (2) Supplicant identify + + Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY); + sendPacket(identifyPacket); + Ethernet radiusIdentifyPacket = fetchPacket(1); + + RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket); + assertThat(radiusAccessRequest, notNullValue()); + assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()), + is("user")); + + IpAddress nasIp = + IpAddress.valueOf(IpAddress.Version.INET, + radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP) + .getValue()); + assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress())); + + // State machine should have been created by now + + StateMachine stateMachine = + StateMachine.lookupStateMachineBySessionId(sessionId); + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); + + // (3) RADIUS MD5 challenge + + Ethernet radiusCodeAccessChallengePacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5); + sendPacket(radiusCodeAccessChallengePacket); + + Ethernet radiusChallengeMD5Packet = fetchPacket(2); + checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5); + + // (4) Supplicant MD5 response + + Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5); + sendPacket(md5RadiusPacket); + Ethernet supplicantMD5ResponsePacket = fetchPacket(3); + RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket); + assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1)); + assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + + // State machine should be in pending state + + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); + + // (5) RADIUS TLS Challenge + + Ethernet radiusCodeAccessChallengeTLSPacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS); + sendPacket(radiusCodeAccessChallengeTLSPacket); + + Ethernet radiusChallengeTLSPacket = fetchPacket(4); + checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS); + + // (6) Supplicant TLS response + + Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS); + sendPacket(tlsRadiusPacket); + Ethernet supplicantTLSResponsePacket = fetchPacket(5); + RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket); + assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0)); + assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + + // (7) RADIUS Success + + Ethernet successPacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS); + sendPacket(successPacket); + Ethernet supplicantSuccessPacket = fetchPacket(6); + + checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS); + + // State machine should be in authorized state + + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED)); } + /** + * Tests the default configuration. + */ + @Test + public void testConfig() { + assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP)); + assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC)); + assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP)); + assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC)); + } } diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java index 2fe44ab9..04837e85 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java @@ -21,6 +21,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.*; public class StateMachineTest { @@ -30,6 +31,7 @@ public class StateMachineTest { public void setUp() { System.out.println("Set Up."); StateMachine.bitSet.clear(); + StateMachine.initializeMaps(); stateMachine = new StateMachine("session0", null); } @@ -37,6 +39,7 @@ public class StateMachineTest { public void tearDown() { System.out.println("Tear Down."); StateMachine.bitSet.clear(); + StateMachine.destroyMaps(); stateMachine = null; } @@ -46,19 +49,19 @@ public class StateMachineTest { */ public void basic() throws StateMachineException { System.out.println("======= BASIC =======."); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test @@ -68,19 +71,19 @@ public class StateMachineTest { public void testIdleState() throws StateMachineException { System.out.println("======= IDLE STATE TEST =======."); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); } @Test @@ -92,19 +95,19 @@ public class StateMachineTest { stateMachine.start(); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); } @Test @@ -118,19 +121,19 @@ public class StateMachineTest { stateMachine.requestAccess(); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); } @Test @@ -144,19 +147,19 @@ public class StateMachineTest { stateMachine.requestAccess(); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); } @Test @@ -170,19 +173,19 @@ public class StateMachineTest { stateMachine.authorizeAccess(); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test @@ -196,27 +199,27 @@ public class StateMachineTest { stateMachine.denyAccess(); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test public void testIdentifierAvailability() throws StateMachineException { System.out.println("======= IDENTIFIER TEST =======."); - byte identifier = stateMachine.getIdentifier(); - System.out.println("State: " + stateMachine.getState()); + byte identifier = stateMachine.identifier(); + System.out.println("State: " + stateMachine.state()); System.out.println("Identifier: " + Byte.toUnsignedInt(identifier)); Assert.assertEquals(-1, identifier); stateMachine.start(); @@ -230,7 +233,7 @@ public class StateMachineTest { for (int i = 1; i <= 255; i++) { StateMachine sm = new StateMachine("session" + i, null); sm.start(); - byte id = sm.getIdentifier(); + byte id = sm.identifier(); Assert.assertEquals(i, Byte.toUnsignedInt(id)); if (i == 3) { sm3 = sm; @@ -244,27 +247,72 @@ public class StateMachineTest { //simulate the state machine for a specific session and logoff so we can free up a spot for an identifier //let's choose identifier 247 then we free up 3 + Assert.assertNotNull(sm247); sm247.requestAccess(); sm247.authorizeAccess(); sm247.logoff(); - sm247 = null; + Assert.assertNotNull(sm3); sm3.requestAccess(); sm3.authorizeAccess(); sm3.logoff(); - sm3 = null; StateMachine otherSM3 = new StateMachine("session3b", null); otherSM3.start(); otherSM3.requestAccess(); - byte id3 = otherSM3.getIdentifier(); + byte id3 = otherSM3.identifier(); Assert.assertEquals(3, Byte.toUnsignedInt(id3)); StateMachine otherSM247 = new StateMachine("session247b", null); otherSM247.start(); otherSM247.requestAccess(); - byte id247 = otherSM247.getIdentifier(); + byte id247 = otherSM247.identifier(); Assert.assertEquals(247, Byte.toUnsignedInt(id247)); + } + @Test + public void testSessionIdLookups() { + String sessionId1 = "session1"; + String sessionId2 = "session2"; + String sessionId3 = "session3"; + + StateMachine machine1ShouldBeNull = + StateMachine.lookupStateMachineBySessionId(sessionId1); + assertNull(machine1ShouldBeNull); + StateMachine machine2ShouldBeNull = + StateMachine.lookupStateMachineBySessionId(sessionId2); + assertNull(machine2ShouldBeNull); + + StateMachine stateMachine1 = new StateMachine(sessionId1, null); + StateMachine stateMachine2 = new StateMachine(sessionId2, null); + + assertEquals(stateMachine1, + StateMachine.lookupStateMachineBySessionId(sessionId1)); + assertEquals(stateMachine2, + StateMachine.lookupStateMachineBySessionId(sessionId2)); + assertNull(StateMachine.lookupStateMachineBySessionId(sessionId3)); + } + + @Test + public void testIdentifierLookups() throws StateMachineException { + String sessionId1 = "session1"; + String sessionId2 = "session2"; + + StateMachine machine1ShouldBeNull = + StateMachine.lookupStateMachineById((byte) 1); + assertNull(machine1ShouldBeNull); + StateMachine machine2ShouldBeNull = + StateMachine.lookupStateMachineById((byte) 2); + assertNull(machine2ShouldBeNull); + + StateMachine stateMachine1 = new StateMachine(sessionId1, null); + stateMachine1.start(); + StateMachine stateMachine2 = new StateMachine(sessionId2, null); + stateMachine2.start(); + + assertEquals(stateMachine1, + StateMachine.lookupStateMachineById(stateMachine1.identifier())); + assertEquals(stateMachine2, + StateMachine.lookupStateMachineById(stateMachine2.identifier())); } } |