diff options
Diffstat (limited to 'framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java')
-rw-r--r-- | framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java | 244 |
1 files changed, 128 insertions, 116 deletions
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 479ec7ed..72a5b122 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,10 +15,13 @@ */ package org.onosproject.aaa; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.net.InetAddress; import java.nio.ByteBuffer; -import java.util.Optional; -import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -30,20 +33,13 @@ 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.Ip4Address; -import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.RADIUS; import org.onlab.packet.RADIUSAttribute; -import org.onlab.packet.TpPort; -import org.onlab.packet.UDP; -import org.onlab.packet.VlanId; 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; @@ -53,7 +49,6 @@ import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.host.HostService; import org.onosproject.net.packet.DefaultOutboundPacket; import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.OutboundPacket; @@ -63,6 +58,8 @@ import org.onosproject.net.packet.PacketService; import org.onosproject.xosintegration.VoltTenantService; import org.slf4j.Logger; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; import static org.onosproject.net.packet.PacketPriority.CONTROL; import static org.slf4j.LoggerFactory.getLogger; @@ -85,10 +82,6 @@ public class AAA { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected PacketService packetService; - // end host information - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostService hostService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VoltTenantService voltTenantService; @@ -121,6 +114,12 @@ public class AAA { // our unique identifier private ApplicationId appId; + // Socket used for UDP communications with RADIUS server + private DatagramSocket radiusSocket; + + // Executor for RADIUS communication thread + private ExecutorService executor; + // Configuration properties factory private final ConfigFactory factory = new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY, @@ -184,7 +183,16 @@ public class AAA { StateMachine.initializeMaps(); - hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); + try { + radiusSocket = new DatagramSocket(radiusServerPort); + } catch (Exception ex) { + log.error("Can't open RADIUS socket", ex); + } + + executor = Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder() + .setNameFormat("AAA-radius-%d").build()); + executor.execute(radiusListener); } @Deactivate @@ -195,6 +203,24 @@ public class AAA { packetService.removeProcessor(processor); processor = null; StateMachine.destroyMaps(); + radiusSocket.close(); + executor.shutdownNow(); + } + + protected void sendRADIUSPacket(RADIUS radiusPacket) { + + try { + final byte[] data = radiusPacket.serialize(); + final DatagramSocket socket = radiusSocket; + + DatagramPacket packet = + new DatagramPacket(data, data.length, + radiusIpAddress, radiusServerPort); + + socket.send(packet); + } catch (IOException e) { + log.info("Cannot send packet to RADIUS server", e); + } } /** @@ -205,14 +231,6 @@ public class AAA { selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort()); packetService.requestPackets(selector.build(), CONTROL, appId); - - TrafficSelector radSelector = DefaultTrafficSelector.builder() - .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) - .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(radiusServerPort)) - .matchUdpSrc(TpPort.tpPort(radiusServerPort)) - .build(); - packetService.requestPackets(radSelector, CONTROL, appId); } /** @@ -222,14 +240,19 @@ public class AAA { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort()); packetService.cancelPackets(selector.build(), CONTROL, appId); + } - TrafficSelector radSelector = DefaultTrafficSelector.builder() - .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) - .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(radiusServerPort)) - .matchUdpSrc(TpPort.tpPort(radiusServerPort)) - .build(); - packetService.cancelPackets(radSelector, CONTROL, appId); + /** + * Send the ethernet packet to the supplicant. + * + * @param ethernetPkt the ethernet packet + * @param connectPoint the connect point to send out + */ + private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) { + TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build(); + OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(), + treatment, ByteBuffer.wrap(ethernetPkt.serialize())); + packetService.emit(packet); } // our handler defined as a private inner class @@ -253,26 +276,11 @@ public class AAA { 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); - handleRadiusPacket(radiusPacket); - } - - break; default: log.trace("Skipping Ethernet packet type {}", EthType.EtherType.lookup(ethPkt.getEtherType())); } - } catch (DeserializationException | StateMachineException e) { + } catch (StateMachineException e) { log.warn("Unable to process RADIUS packet:", e); } } @@ -280,23 +288,26 @@ public class AAA { /** * Creates and initializes common fields of a RADIUS packet. * - * @param identifier RADIUS identifier + * @param stateMachine state machine for the request * @param eapPacket EAP packet * @return RADIUS packet */ - private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) { + private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) { RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, eapPacket.getIdentifier()); + + // set Request Authenticator in StateMachine + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); + radiusPayload.setIdentifier(identifier); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - eapPacket.getData()); + stateMachine.username()); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, AAA.this.nasIpAddress.getAddress()); radiusPayload.encapsulateMessage(eapPacket); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); return radiusPayload; } @@ -329,13 +340,13 @@ public class AAA { //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), + Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(nasMacAddress), ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, eapPayload); stateMachine.setSupplicantAddress(srcMAC); stateMachine.setVlanId(ethPkt.getVlanID()); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case EAPOL.EAPOL_PACKET: @@ -350,11 +361,10 @@ public class AAA { // request id access to RADIUS stateMachine.setUsername(eapPacket.getData()); - radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); + radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket); + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); - // set Request Authenticator in StateMachine - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - sendRadiusMessage(radiusPayload); + sendRADIUSPacket(radiusPayload); // change the state to "PENDING" stateMachine.requestAccess(); @@ -365,24 +375,31 @@ public class AAA { // machine. if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) { //send the RADIUS challenge response - radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket); + radiusPayload = + getRadiusPayload(stateMachine, + stateMachine.identifier(), + eapPacket); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, stateMachine.challengeState()); - sendRadiusMessage(radiusPayload); + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + sendRADIUSPacket(radiusPayload); } break; case EAP.ATTR_TLS: // request id access to RADIUS - radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); + radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, stateMachine.challengeState()); stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - sendRadiusMessage(radiusPayload); - // TODO: this gets called on every fragment, should only be called at TLS-Start - stateMachine.requestAccess(); + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + sendRADIUSPacket(radiusPayload); + + if (stateMachine.state() != StateMachine.STATE_PENDING) { + stateMachine.requestAccess(); + } break; default: @@ -392,14 +409,18 @@ public class AAA { default: log.trace("Skipping EAPOL message {}", eapol.getEapolType()); } + } + } + + class RadiusListener implements Runnable { /** * Handles RADIUS packets. * * @param radiusPacket RADIUS packet coming from the RADIUS server. */ - private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException { + protected void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException { StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier()); if (stateMachine == null) { log.error("Invalid session identifier, exiting..."); @@ -410,13 +431,16 @@ public class AAA { Ethernet eth; switch (radiusPacket.getCode()) { case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE: - byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue(); + 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, + MacAddress.valueOf(nasMacAddress), + stateMachine.vlanId(), + EAPOL.EAPOL_PACKET, eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case RADIUS.RADIUS_CODE_ACCESS_ACCEPT: //send an EAPOL - Success to the supplicant. @@ -425,9 +449,11 @@ public class AAA { eapPayload = new EAP(); eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); eth = buildEapolResponse(stateMachine.supplicantAddress(), - MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, + MacAddress.valueOf(nasMacAddress), + stateMachine.vlanId(), + EAPOL.EAPOL_PACKET, eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); stateMachine.authorizeAccess(); break; @@ -439,59 +465,45 @@ public class AAA { } } - 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(); - - Host dst; - if (!odst.isPresent()) { - log.info("Radius server {} is not present", radiusIpAddress); - return; - } else { - dst = odst.get(); - } - - UDP udp = new UDP(); - IPv4 ip4Packet = new IPv4(); - Ethernet ethPkt = new Ethernet(); - radiusMessage.setParent(udp); - udp.setDestinationPort(radiusServerPort); - udp.setSourcePort(radiusServerPort); - udp.setPayload(radiusMessage); - udp.setParent(ip4Packet); - ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress()); - ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress()); - ip4Packet.setProtocol(IPv4.PROTOCOL_UDP); - ip4Packet.setPayload(udp); - ip4Packet.setParent(ethPkt); - ethPkt.setDestinationMACAddress(radiusMacAddress); - ethPkt.setSourceMACAddress(nasMacAddress); - ethPkt.setEtherType(Ethernet.TYPE_IPV4); - ethPkt.setPayload(ip4Packet); - - TrafficTreatment treatment = DefaultTrafficTreatment.builder() - .setOutput(PortNumber.portNumber(radiusPort)).build(); - OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch), - treatment, ByteBuffer.wrap(ethPkt.serialize())); - packetService.emit(packet); - } + @Override + public void run() { + boolean done = false; + int packetNumber = 1; + + log.info("UDP listener thread starting up"); + RADIUS inboundRadiusPacket; + while (!done) { + try { + byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH]; + DatagramPacket inboundBasePacket = + new DatagramPacket(packetBuffer, packetBuffer.length); + DatagramSocket socket = radiusSocket; + socket.receive(inboundBasePacket); + log.info("Packet #{} received", packetNumber++); + try { + inboundRadiusPacket = + RADIUS.deserializer() + .deserialize(inboundBasePacket.getData(), + 0, + inboundBasePacket.getLength()); + handleRadiusPacket(inboundRadiusPacket); + } catch (DeserializationException dex) { + log.error("Cannot deserialize packet", dex); + } catch (StateMachineException sme) { + log.error("Illegal state machine operation", sme); + } - /** - * Send the ethernet packet to the supplicant. - * - * @param ethernetPkt the ethernet packet - * @param connectPoint the connect point to send out - */ - private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) { - TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build(); - OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(), - treatment, ByteBuffer.wrap(ethernetPkt.serialize())); - packetService.emit(packet); + } catch (IOException e) { + log.info("Socket was closed, exiting listener thread"); + done = true; + } + } } - } + RadiusListener radiusListener = new RadiusListener(); + private class InternalConfigListener implements NetworkConfigListener { /** |