diff options
Diffstat (limited to 'framework/src/onos/apps')
34 files changed, 1530 insertions, 518 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 { /** 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 index c18d2bf2..73be7691 100644 --- 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 @@ -37,13 +37,13 @@ public class AAAConfig extends Config<ApplicationId> { private static final String RADIUS_PORT = "radiusPort"; // RADIUS server IP address - protected static final String DEFAULT_RADIUS_IP = "192.168.1.10"; + protected static final String DEFAULT_RADIUS_IP = "10.128.10.4"; // 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"; + protected static final String DEFAULT_NAS_IP = "10.128.9.244"; // NAS MAC address protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01"; diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java new file mode 100644 index 00000000..fb513ced --- /dev/null +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java @@ -0,0 +1,151 @@ +/* + * Copyright 2014 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 org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onlab.packet.EAP; +import org.onlab.packet.EAPOL; +import org.onlab.packet.Ethernet; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.NetworkConfigRegistryAdapter; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * Set of tests of the ONOS application component. These use an existing RADIUS + * server and sends live packets over the network to it. + */ +@Ignore ("This should not be run as part of the standard build") +public class AAAIntegrationTest extends AAATestBase { + + private AAA aaa; + + /** + * Mocks the network config registry. + */ + @SuppressWarnings("unchecked") + static final class TestNetworkConfigRegistry + extends NetworkConfigRegistryAdapter { + @Override + public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) { + return (C) new AAAConfig(); + } + } + + /** + * 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.activate(); + } + + /** + * 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) { + for (int iteration = 0; iteration < 20; iteration++) { + if (savedPackets.size() > index) { + return (Ethernet) savedPackets.get(index); + } else { + try { + Thread.sleep(250); + } catch (Exception ex) { + return null; + } + } + } + return null; + } + + /** + * Tests the authentication path through the AAA application by sending + * packets to the RADIUS server and checking the state machine + * transitions. + * + * @throws Exception when an unhandled error occurs + */ + @Test + public void testAuthentication() throws Exception { + + // (1) Supplicant start up + + Ethernet startPacket = constructSupplicantStartPacket(); + sendPacket(startPacket); + + Ethernet responsePacket = fetchPacket(0); + assertThat(responsePacket, notNullValue()); + checkRadiusPacket(aaa, responsePacket, EAP.REQUEST); + + // (2) Supplicant identify + + Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null); + sendPacket(identifyPacket); + + // State machine should have been created by now + + StateMachine stateMachine = + StateMachine.lookupStateMachineBySessionId(SESSION_ID); + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); + + // (3) RADIUS MD5 challenge + + Ethernet radiusChallengeMD5Packet = fetchPacket(1); + assertThat(radiusChallengeMD5Packet, notNullValue()); + checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.REQUEST); + + + // (4) Supplicant MD5 response + + Ethernet md5RadiusPacket = + constructSupplicantIdentifyPacket(stateMachine, + EAP.ATTR_MD5, + stateMachine.challengeIdentifier(), + radiusChallengeMD5Packet); + sendPacket(md5RadiusPacket); + + + // (5) RADIUS Success + + Ethernet successRadiusPacket = fetchPacket(2); + assertThat(successRadiusPacket, notNullValue()); + EAPOL successEAPOL = (EAPOL) successRadiusPacket.getPayload(); + EAP successEAP = (EAP) successEAPOL.getPayload(); + assertThat(successEAP.getCode(), is(EAP.SUCCESS)); + + // State machine should be in authorized state + + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED)); + + } + +} + 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 75a60336..860a7dbd 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,164 +15,61 @@ */ package org.onosproject.aaa; -import java.nio.ByteBuffer; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import java.net.InetAddress; +import java.net.UnknownHostException; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.onlab.packet.Data; +import org.onlab.packet.BasePacket; 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 { +public class AAATest extends AAATestBase { - MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); - MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a"); + static final String BAD_IP_ADDRESS = "198.51.100.0"; - 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); + class AAAWithoutRadiusServer extends AAA { + protected void sendRADIUSPacket(RADIUS radiusPacket) { + savePacket(radiusPacket); + } } /** - * Keeps a reference to the PacketProcessor and saves the OutboundPackets. + * Mocks the AAAConfig class to force usage of an unroutable address for the + * RADIUS server. */ - private class MockPacketService extends PacketServiceAdapter { - - @Override - public void addProcessor(PacketProcessor processor, int priority) { - packetProcessor = processor; - } - + static class MockAAAConfig extends AAAConfig { @Override - public void emit(OutboundPacket packet) { + public InetAddress radiusIp() { try { - Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(), - 0, packet.data().array().length); - savePacket(eth); - } catch (Exception e) { - fail(e.getMessage()); + return InetAddress.getByName(BAD_IP_ADDRESS); + } catch (UnknownHostException ex) { + // can't happen + throw new IllegalStateException(ex); } } } /** - * 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") @@ -180,83 +77,12 @@ public class AAATest { extends NetworkConfigRegistryAdapter { @Override public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) { - return (C) new AAAConfig(); + AAAConfig aaaConfig = new MockAAAConfig(); + return (C) 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. * @@ -264,18 +90,9 @@ public class AAATest { * @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()); + private RADIUS constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) { - String challenge = "1234"; + String challenge = "12345678901234567"; EAP eap = new EAP(challengeType, (byte) 1, challengeType, challenge.getBytes(Charsets.US_ASCII)); @@ -291,13 +108,7 @@ public class AAATest { 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; + return radius; } /** @@ -305,11 +116,10 @@ public class AAATest { */ @Before public void setUp() { - aaa = new AAA(); + aaa = new AAAWithoutRadiusServer(); aaa.netCfgService = new TestNetworkConfigRegistry(); aaa.coreService = new CoreServiceAdapter(); aaa.packetService = new MockPacketService(); - aaa.hostService = new MockHostService(); aaa.activate(); } @@ -324,60 +134,16 @@ public class AAATest { /** * Extracts the RADIUS packet from a packet sent by the supplicant. * - * @param supplicantPacket packet sent by the supplicant - * @return RADIUS packet + * @param radius RADIUS packet sent by the supplicant * @throws DeserializationException if deserialization of the packet contents * fails. */ - private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket) + private void checkRADIUSPacketFromSupplicant(RADIUS radius) 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(); + EAP eap = radius.decapsulateMessage(); assertThat(eap, notNullValue()); - assertThat(eap.getCode(), is(code)); } /** @@ -387,11 +153,10 @@ public class AAATest { * @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; + private BasePacket fetchPacket(int index) { + BasePacket packet = savedPackets.get(index); + assertThat(packet, notNullValue()); + return packet; } /** @@ -400,61 +165,64 @@ public class AAATest { * @throws DeserializationException if packed deserialization fails. */ @Test - public void testAuthentication() throws DeserializationException { - - // Our session id will be the device ID ("of:1") with the port ("1") concatenated - String sessionId = "of:11"; + public void testAuthentication() throws Exception { // (1) Supplicant start up Ethernet startPacket = constructSupplicantStartPacket(); sendPacket(startPacket); - Ethernet responsePacket = fetchPacket(0); - checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY); + Ethernet responsePacket = (Ethernet) fetchPacket(0); + checkRadiusPacket(aaa, responsePacket, EAP.ATTR_IDENTITY); // (2) Supplicant identify - Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY); + Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null); sendPacket(identifyPacket); - Ethernet radiusIdentifyPacket = fetchPacket(1); + RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1); + + checkRADIUSPacketFromSupplicant(radiusIdentifyPacket); - 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")); + assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()), + is("testuser")); IpAddress nasIp = IpAddress.valueOf(IpAddress.Version.INET, - radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP) + radiusIdentifyPacket.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); + StateMachine.lookupStateMachineBySessionId(SESSION_ID); assertThat(stateMachine, notNullValue()); assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); // (3) RADIUS MD5 challenge - Ethernet radiusCodeAccessChallengePacket = + RADIUS radiusCodeAccessChallengePacket = constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5); - sendPacket(radiusCodeAccessChallengePacket); + aaa.radiusListener.handleRadiusPacket(radiusCodeAccessChallengePacket); - Ethernet radiusChallengeMD5Packet = fetchPacket(2); - checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5); + Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2); + checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.ATTR_MD5); // (4) Supplicant MD5 response - Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5); + Ethernet md5RadiusPacket = + constructSupplicantIdentifyPacket(stateMachine, + EAP.ATTR_MD5, + stateMachine.challengeIdentifier(), + radiusChallengeMD5Packet); sendPacket(md5RadiusPacket); - Ethernet supplicantMD5ResponsePacket = fetchPacket(3); - RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket); - assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1)); + + RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3); + + checkRADIUSPacketFromSupplicant(responseMd5RadiusPacket); + assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 0)); assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); // State machine should be in pending state @@ -462,37 +230,20 @@ public class AAATest { 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); + // (5) RADIUS Success - 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 = + RADIUS successPacket = constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS); - sendPacket(successPacket); - Ethernet supplicantSuccessPacket = fetchPacket(6); + aaa.radiusListener.handleRadiusPacket((successPacket)); + Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4); - checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS); + checkRadiusPacket(aaa, supplicantSuccessPacket, EAP.SUCCESS); // State machine should be in authorized state assertThat(stateMachine, notNullValue()); assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED)); + } /** @@ -502,7 +253,7 @@ public class AAATest { 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.radiusIpAddress.getHostAddress(), is(BAD_IP_ADDRESS)); assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC)); } } diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java new file mode 100644 index 00000000..dffcba2f --- /dev/null +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java @@ -0,0 +1,224 @@ +/* + * 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.nio.ByteBuffer; +import java.security.MessageDigest; +import java.util.LinkedList; +import java.util.List; + +import org.onlab.packet.BasePacket; +import org.onlab.packet.EAP; +import org.onlab.packet.EAPOL; +import org.onlab.packet.EthType; +import org.onlab.packet.Ethernet; +import org.onlab.packet.MacAddress; +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 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; + +/** + * Common methods for AAA app testing. + */ +public class AAATestBase { + + MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); + MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a"); + + // Our session id will be the device ID ("of:1") with the port ("1") concatenated + static final String SESSION_ID = "of:11"; + + List<BasePacket> savedPackets = new LinkedList<>(); + PacketProcessor packetProcessor; + + /** + * Saves the given packet onto the saved packets list. + * + * @param packet packet to save + */ + void savePacket(BasePacket packet) { + savedPackets.add(packet); + } + + /** + * Keeps a reference to the PacketProcessor and saves the OutboundPackets. + */ + 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. + */ + 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. + } + } + + /** + * Sends an Ethernet packet to the process method of the Packet Processor. + * + * @param reply Ethernet packet + */ + 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 identification payload. + * + * @return Ethernet packet + */ + Ethernet constructSupplicantIdentifyPacket(StateMachine stateMachine, + byte type, + byte id, + Ethernet radiusChallenge) + throws Exception { + 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 = "testuser"; + byte[] data = username.getBytes(); + + + if (type == EAP.ATTR_MD5) { + String password = "testpassword"; + EAPOL eapol = (EAPOL) radiusChallenge.getPayload(); + EAP eap = (EAP) eapol.getPayload(); + + byte[] identifier = new byte[password.length() + eap.getData().length]; + + identifier[0] = stateMachine.challengeIdentifier(); + System.arraycopy(password.getBytes(), 0, identifier, 1, password.length()); + System.arraycopy(eap.getData(), 1, identifier, 1 + password.length(), 16); + + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] hash = md.digest(identifier); + data = new byte[17]; + data[0] = (byte) 16; + System.arraycopy(hash, 0, data, 1, 16); + } + EAP eap = new EAP(EAP.RESPONSE, (byte) 1, type, + data); + eap.setIdentifier(id); + + // 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 EAPOL_START Payload. + * + * @return Ethernet packet + */ + 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) 2, 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; + } + + /** + * Checks the contents of a RADIUS packet being sent to the RADIUS server. + * + * @param radiusPacket packet to check + * @param code expected code + */ + void checkRadiusPacket(AAA aaa, Ethernet radiusPacket, byte code) { + + assertThat(radiusPacket.getSourceMAC(), + is(MacAddress.valueOf(aaa.nasMacAddress))); + 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)); + } +} 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 04837e85..1838c63e 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,7 +21,9 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class StateMachineTest { diff --git a/framework/src/onos/apps/cordvtn/pom.xml b/framework/src/onos/apps/cordvtn/pom.xml index b8e913d4..1d96108b 100644 --- a/framework/src/onos/apps/cordvtn/pom.xml +++ b/framework/src/onos/apps/cordvtn/pom.xml @@ -54,6 +54,16 @@ <artifactId>onos-ovsdb-api</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + <version>3.0.3</version> + </dependency> </dependencies> </project> diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java index ba707800..4b28a14b 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java @@ -27,7 +27,6 @@ import org.onlab.util.KryoNamespace; import org.onosproject.cluster.ClusterService; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; -import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; @@ -52,6 +51,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -75,7 +75,8 @@ public class CordVtn implements CordVtnService { .register(KryoNamespaces.API) .register(DefaultOvsdbNode.class); private static final String DEFAULT_BRIDGE_NAME = "br-int"; - private static final Map<String, String> VXLAN_OPTIONS = new HashMap<String, String>() { + private static final String DEFAULT_TUNNEL = "vxlan"; + private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() { { put("key", "flow"); put("local_ip", "flow"); @@ -98,9 +99,6 @@ public class CordVtn implements CordVtnService { protected HostService hostService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected MastershipService mastershipService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected OvsdbController controller; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @@ -117,11 +115,10 @@ public class CordVtn implements CordVtnService { private final VmHandler vmHandler = new VmHandler(); private ConsistentMap<DeviceId, OvsdbNode> nodeStore; - private ApplicationId appId; @Activate protected void activate() { - appId = coreService.registerApplication("org.onosproject.cordvtn"); + ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn"); nodeStore = storageService.<DeviceId, OvsdbNode>consistentMapBuilder() .withSerializer(Serializer.using(NODE_SERIALIZER.build())) .withName("cordvtn-nodestore") @@ -148,38 +145,59 @@ public class CordVtn implements CordVtnService { @Override public void addNode(OvsdbNode ovsdb) { checkNotNull(ovsdb); - nodeStore.put(ovsdb.deviceId(), ovsdb); + + nodeStore.putIfAbsent(ovsdb.deviceId(), ovsdb); + + if (isNodeConnected(ovsdb)) { + init(ovsdb); + } else { + connect(ovsdb); + } } @Override public void deleteNode(OvsdbNode ovsdb) { checkNotNull(ovsdb); + if (deviceService.getDevice(ovsdb.deviceId()) != null) { + if (deviceService.isAvailable(ovsdb.deviceId())) { + log.warn("Cannot delete connected node {}", ovsdb.host()); + return; + } + } + nodeStore.remove(ovsdb.deviceId()); + } + + @Override + public void connect(OvsdbNode ovsdb) { + checkNotNull(ovsdb); + if (!nodeStore.containsKey(ovsdb.deviceId())) { + log.warn("Node {} does not exist", ovsdb.host()); return; } - // check ovsdb and integration bridge connection state first - if (isNodeConnected(ovsdb)) { - log.warn("Cannot delete connected node {}", ovsdb.host()); - } else { - nodeStore.remove(ovsdb.deviceId()); + if (!isNodeConnected(ovsdb)) { + controller.connect(ovsdb.ip(), ovsdb.port()); } } @Override - public void connect(OvsdbNode ovsdb) { + public void disconnect(OvsdbNode ovsdb) { checkNotNull(ovsdb); if (!nodeStore.containsKey(ovsdb.deviceId())) { log.warn("Node {} does not exist", ovsdb.host()); return; } - controller.connect(ovsdb.ip(), ovsdb.port()); + + if (isNodeConnected(ovsdb)) { + OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); + ovsdbClient.disconnect(); + } } - @Override - public void disconnect(OvsdbNode ovsdb) { + private void init(OvsdbNode ovsdb) { checkNotNull(ovsdb); if (!nodeStore.containsKey(ovsdb.deviceId())) { @@ -187,11 +205,16 @@ public class CordVtn implements CordVtnService { return; } - OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); - checkNotNull(ovsdbClient); + if (!isNodeConnected(ovsdb)) { + log.warn("Node {} is not connected", ovsdb.host()); + return; + } - if (ovsdbClient.isConnected()) { - ovsdbClient.disconnect(); + if (deviceService.getDevice(ovsdb.intBrId()) == null || + !deviceService.isAvailable(ovsdb.intBrId())) { + createIntegrationBridge(ovsdb); + } else if (!checkVxlanPort(ovsdb)) { + createVxlanPort(ovsdb); } } @@ -235,11 +258,45 @@ public class CordVtn implements CordVtnService { OvsdbClientService ovsdbClient = controller.getOvsdbClient( new OvsdbNodeId(ovsdb.ip(), ovsdb.port().toInt())); if (ovsdbClient == null) { - log.warn("Couldn't find ovsdb client of node {}", ovsdb.host()); + log.debug("Couldn't find ovsdb client for {}", ovsdb.host()); } return ovsdbClient; } + private void createIntegrationBridge(OvsdbNode ovsdb) { + List<ControllerInfo> controllers = new ArrayList<>(); + Sets.newHashSet(clusterService.getNodes()) + .forEach(controller -> { + ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp"); + controllers.add(ctrlInfo); + }); + String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN); + + // TODO change to use bridge config + OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); + ovsdbClient.createBridge(DEFAULT_BRIDGE_NAME, dpid, controllers); + } + + private void createVxlanPort(OvsdbNode ovsdb) { + // TODO change to use tunnel config and tunnel description + OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); + ovsdbClient.createTunnel(DEFAULT_BRIDGE_NAME, DEFAULT_TUNNEL, + DEFAULT_TUNNEL, DEFAULT_TUNNEL_OPTIONS); + } + + private boolean checkVxlanPort(OvsdbNode ovsdb) { + // TODO change to use tunnel config + OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); + try { + ovsdbClient.getPorts().stream() + .filter(p -> p.portName().value().equals(DEFAULT_TUNNEL)) + .findFirst().get(); + } catch (NoSuchElementException e) { + return false; + } + return true; + } + private class InternalDeviceListener implements DeviceListener { @Override @@ -252,8 +309,11 @@ public class CordVtn implements CordVtnService { eventExecutor.submit(() -> handler.connected(device)); break; case DEVICE_AVAILABILITY_CHANGED: - eventExecutor.submit(() -> handler.disconnected(device)); - // TODO handle the case that the device is recovered + if (deviceService.isAvailable(device.id())) { + eventExecutor.submit(() -> handler.connected(device)); + } else { + eventExecutor.submit(() -> handler.disconnected(device)); + } break; default: break; @@ -286,20 +346,10 @@ public class CordVtn implements CordVtnService { public void connected(Device device) { log.info("Ovsdb {} is connected", device.id()); - if (!mastershipService.isLocalMaster(device.id())) { - return; - } - - // TODO change to use bridge config OvsdbNode ovsdb = getNode(device.id()); - OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); - - List<ControllerInfo> controllers = new ArrayList<>(); - Sets.newHashSet(clusterService.getNodes()).forEach(controller -> - controllers.add(new ControllerInfo(controller.ip(), OFPORT, "tcp"))); - String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN); - - ovsdbClient.createBridge(DEFAULT_BRIDGE_NAME, dpid, controllers); + if (ovsdb != null) { + init(ovsdb); + } } @Override @@ -314,22 +364,19 @@ public class CordVtn implements CordVtnService { public void connected(Device device) { log.info("Integration Bridge {} is detected", device.id()); - OvsdbNode ovsdb = getNodes().stream() - .filter(node -> node.intBrId().equals(device.id())) - .findFirst().get(); - - if (ovsdb == null) { + OvsdbNode ovsdb; + try { + ovsdb = getNodes().stream() + .filter(node -> node.intBrId().equals(device.id())) + .findFirst().get(); + } catch (NoSuchElementException e) { log.warn("Couldn't find OVSDB associated with {}", device.id()); return; } - if (!mastershipService.isLocalMaster(ovsdb.deviceId())) { - return; + if (!checkVxlanPort(ovsdb)) { + createVxlanPort(ovsdb); } - - // TODO change to use tunnel config and tunnel description - OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb); - ovsdbClient.createTunnel(DEFAULT_BRIDGE_NAME, "vxlan", "vxlan", VXLAN_OPTIONS); } @Override diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java index f276c7ca..287f2a34 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java @@ -94,7 +94,6 @@ public class CordVtnConfigManager { DefaultOvsdbNode ovsdb = new DefaultOvsdbNode( node.host(), node.ip(), node.port(), node.bridgeId()); cordVtnService.addNode(ovsdb); - cordVtnService.connect(ovsdb); }); } diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java index c5b7a078..7a9a06a6 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java @@ -19,11 +19,20 @@ import org.onlab.packet.IpAddress; import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; +import java.util.Comparator; + /** * Representation of a node with ovsdb server. */ public interface OvsdbNode { + Comparator<OvsdbNode> OVSDB_NODE_COMPARATOR = new Comparator<OvsdbNode>() { + @Override + public int compare(OvsdbNode ovsdb1, OvsdbNode ovsdb2) { + return ovsdb1.host().compareTo(ovsdb2.host()); + } + }; + /** * Returns the IP address of the ovsdb server. * diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java new file mode 100644 index 00000000..88d16341 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java @@ -0,0 +1,65 @@ +/* + * 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.cordvtn.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cordvtn.CordVtnService; +import org.onosproject.cordvtn.DefaultOvsdbNode; +import org.onosproject.cordvtn.OvsdbNode; +import org.onosproject.net.DeviceId; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Adds a new OVSDB nodes. + */ +@Command(scope = "onos", name = "ovsdb-add", + description = "Adds a new OVSDB node to cordvtn") +public class OvsdbNodeAddCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "host", description = "Hostname or IP", + required = true, multiValued = false) + private String host = null; + + @Argument(index = 1, name = "address", + description = "OVSDB server listening address (ip:port)", + required = true, multiValued = false) + private String address = null; + + @Argument(index = 2, name = "bridgeId", + description = "Device ID of integration bridge", + required = true, multiValued = false) + private String bridgeId = null; + + @Override + protected void execute() { + checkArgument(address.contains(":"), "address should be ip:port format"); + checkArgument(bridgeId.startsWith("of:"), "bridgeId should be of:dpid format"); + + CordVtnService service = AbstractShellCommand.get(CordVtnService.class); + String[] ipPort = address.split(":"); + OvsdbNode ovsdb = new DefaultOvsdbNode(host, + IpAddress.valueOf(ipPort[0]), + TpPort.tpPort(Integer.parseInt(ipPort[1])), + DeviceId.deviceId(bridgeId)); + service.addNode(ovsdb); + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java new file mode 100644 index 00000000..e4ca0f3c --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java @@ -0,0 +1,60 @@ +/* + * 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.cordvtn.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cordvtn.CordVtnService; +import org.onosproject.cordvtn.OvsdbNode; + +import java.util.NoSuchElementException; + +/** + * Connects to OVSDBs. + */ +@Command(scope = "onos", name = "ovsdb-connect", + description = "Connects to OVSDBs") +public class OvsdbNodeConnectCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)", + required = true, multiValued = true) + private String[] hosts = null; + + @Override + protected void execute() { + CordVtnService service = AbstractShellCommand.get(CordVtnService.class); + + for (String host : hosts) { + OvsdbNode ovsdb; + try { + ovsdb = service.getNodes().stream() + .filter(node -> node.host().equals(host)) + .findFirst().get(); + } catch (NoSuchElementException e) { + print("Unable to find %s", host); + continue; + } + + if (service.isNodeConnected(ovsdb)) { + print("OVSDB %s is already in connected state, do nothing", host); + } else { + service.connect(ovsdb); + } + } + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java new file mode 100644 index 00000000..a500d0d8 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java @@ -0,0 +1,57 @@ +/* + * 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.cordvtn.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cordvtn.CordVtnService; +import org.onosproject.cordvtn.OvsdbNode; + +import java.util.NoSuchElementException; + +/** + * Deletes OVSDB nodes from cordvtn. + */ +@Command(scope = "onos", name = "ovsdb-delete", + description = "Deletes OVSDB nodes from cordvtn") +public class OvsdbNodeDeleteCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)", + required = true, multiValued = true) + private String[] hosts = null; + + @Override + protected void execute() { + CordVtnService service = AbstractShellCommand.get(CordVtnService.class); + + for (String host : hosts) { + OvsdbNode ovsdb; + try { + ovsdb = service.getNodes().stream() + .filter(node -> node.host().equals(host)) + .findFirst().get(); + + } catch (NoSuchElementException e) { + print("Unable to find %s", host); + continue; + } + + service.deleteNode(ovsdb); + } + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java new file mode 100644 index 00000000..14e44e08 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java @@ -0,0 +1,60 @@ +/* + * 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.cordvtn.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cordvtn.CordVtnService; +import org.onosproject.cordvtn.OvsdbNode; + +import java.util.NoSuchElementException; + +/** + * Disconnects OVSDBs. + */ +@Command(scope = "onos", name = "ovsdb-disconnect", + description = "Disconnects OVSDBs") +public class OvsdbNodeDisconnectCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)", + required = true, multiValued = true) + private String[] hosts = null; + + @Override + protected void execute() { + CordVtnService service = AbstractShellCommand.get(CordVtnService.class); + + for (String host : hosts) { + OvsdbNode ovsdb; + try { + ovsdb = service.getNodes().stream() + .filter(node -> node.host().equals(host)) + .findFirst().get(); + } catch (NoSuchElementException e) { + print("Unable to find %s", host); + continue; + } + + if (!service.isNodeConnected(ovsdb)) { + print("OVSDB %s is already in disconnected state, do nothing", host); + } else { + service.disconnect(ovsdb); + } + } + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java new file mode 100644 index 00000000..7d125ca6 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java @@ -0,0 +1,74 @@ +/* + * 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.cordvtn.cli; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cordvtn.CordVtnService; +import org.onosproject.cordvtn.OvsdbNode; + +import java.util.Collections; +import java.util.List; + +/** + * Lists all OVSDB nodes. + */ +@Command(scope = "onos", name = "ovsdbs", + description = "Lists all OVSDB nodes registered in cordvtn application") +public class OvsdbNodeListCommand extends AbstractShellCommand { + + @Override + protected void execute() { + CordVtnService service = AbstractShellCommand.get(CordVtnService.class); + List<OvsdbNode> ovsdbs = service.getNodes(); + Collections.sort(ovsdbs, OvsdbNode.OVSDB_NODE_COMPARATOR); + + if (outputJson()) { + print("%s", json(service, ovsdbs)); + } else { + for (OvsdbNode ovsdb : ovsdbs) { + print("host=%s, address=%s, br-int=%s, state=%s", + ovsdb.host(), + ovsdb.ip().toString() + ":" + ovsdb.port().toString(), + ovsdb.intBrId().toString(), + getState(service, ovsdb)); + } + print("Total %s nodes", service.getNodeCount()); + } + } + + private JsonNode json(CordVtnService service, List<OvsdbNode> ovsdbs) { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode result = mapper.createArrayNode(); + for (OvsdbNode ovsdb : ovsdbs) { + String ipPort = ovsdb.ip().toString() + ":" + ovsdb.port().toString(); + result.add(mapper.createObjectNode() + .put("host", ovsdb.host()) + .put("address", ipPort) + .put("brInt", ovsdb.intBrId().toString()) + .put("state", getState(service, ovsdb))); + } + return result; + } + + private String getState(CordVtnService service, OvsdbNode ovsdb) { + return service.isNodeConnected(ovsdb) ? "CONNECTED" : "DISCONNECTED"; + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java new file mode 100644 index 00000000..686172ce --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Console commands to manage OVSDB nodes for cordvtn. + */ +package org.onosproject.cordvtn.cli;
\ No newline at end of file diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java new file mode 100644 index 00000000..1c13737f --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Application for provisioning virtual tenant networks. + */ +package org.onosproject.cordvtn;
\ No newline at end of file diff --git a/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..6e172387 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,35 @@ +<!-- + ~ 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. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.cordvtn.cli.OvsdbNodeListCommand"/> + </command> + <command> + <action class="org.onosproject.cordvtn.cli.OvsdbNodeAddCommand"/> + </command> + <command> + <action class="org.onosproject.cordvtn.cli.OvsdbNodeDeleteCommand"/> + </command> + <command> + <action class="org.onosproject.cordvtn.cli.OvsdbNodeConnectCommand"/> + </command> + <command> + <action class="org.onosproject.cordvtn.cli.OvsdbNodeDisconnectCommand"/> + </command> + </command-bundle> +</blueprint> diff --git a/framework/src/onos/apps/olt/pom.xml b/framework/src/onos/apps/olt/pom.xml index 180e026a..f803a61a 100644 --- a/framework/src/onos/apps/olt/pom.xml +++ b/framework/src/onos/apps/olt/pom.xml @@ -36,6 +36,15 @@ </properties> <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java new file mode 100644 index 00000000..90ed7403 --- /dev/null +++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java @@ -0,0 +1,43 @@ +/* + * 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.olt; + +import org.onlab.packet.VlanId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.config.Config; + +/** + * Config object for access device data. + */ +public class AccessDeviceConfig extends Config<DeviceId> { + + private static final String UPLINK = "uplink"; + private static final String VLAN = "vlan"; + + /** + * Gets the access device configuration for this device. + * + * @return access device configuration + */ + public AccessDeviceData getOlt() { + PortNumber uplink = PortNumber.portNumber(node.path(UPLINK).asText()); + VlanId vlan = VlanId.vlanId(Short.parseShort(node.path(VLAN).asText())); + + return new AccessDeviceData(subject(), uplink, vlan); + } +} diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java new file mode 100644 index 00000000..f7e40e30 --- /dev/null +++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java @@ -0,0 +1,76 @@ +/* + * 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.olt; + +import org.onlab.packet.VlanId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Information about an access device. + */ +public class AccessDeviceData { + private static final String DEVICE_ID_MISSING = "Device ID cannot be null"; + private static final String UPLINK_MISSING = "Uplink cannot be null"; + private static final String VLAN_MISSING = "VLAN ID cannot be null"; + + private final DeviceId deviceId; + private final PortNumber uplink; + private final VlanId vlan; + + /** + * Class constructor. + * + * @param deviceId access device ID + * @param uplink uplink port number + * @param vlan device VLAN ID + */ + public AccessDeviceData(DeviceId deviceId, PortNumber uplink, VlanId vlan) { + this.deviceId = checkNotNull(deviceId, DEVICE_ID_MISSING); + this.uplink = checkNotNull(uplink, UPLINK_MISSING); + this.vlan = checkNotNull(vlan, VLAN_MISSING); + } + + /** + * Retrieves the access device ID. + * + * @return device ID + */ + public DeviceId deviceId() { + return deviceId; + } + + /** + * Retrieves the uplink port number. + * + * @return port number + */ + public PortNumber uplink() { + return uplink; + } + + /** + * Retrieves the VLAN ID assigned to the device. + * + * @return vlan ID + */ + public VlanId vlan() { + return vlan; + } +} diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java new file mode 100644 index 00000000..bd82f489 --- /dev/null +++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java @@ -0,0 +1,41 @@ +/* + * 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.olt; + +import org.onlab.packet.VlanId; +import org.onosproject.net.ConnectPoint; + +/** + * Service for interacting with an access device (OLT). + */ +public interface AccessDeviceService { + + /** + * Provisions connectivity for a subscriber on an access device. + * + * @param port subscriber's connection point + * @param vlan VLAN ID to provision for subscriber + */ + void provisionSubscriber(ConnectPoint port, VlanId vlan); + + /** + * Removes provisioned connectivity for a subscriber from an access device. + * + * @param port subscriber's connection point + */ + void removeSubscriber(ConnectPoint port); +} diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java index c92f47a2..9aa8865a 100644 --- a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java +++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java @@ -15,7 +15,6 @@ */ package org.onosproject.olt; - import com.google.common.base.Strings; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -24,13 +23,20 @@ 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.apache.felix.scr.annotations.Service; import org.onlab.packet.VlanId; import org.onlab.util.Tools; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Port; 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.config.basics.SubjectFactories; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -45,15 +51,17 @@ import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import java.util.Dictionary; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import static org.slf4j.LoggerFactory.getLogger; /** - * Sample mobility application. Cleans up flowmods when a host moves. + * Provisions rules on access devices. */ +@Service @Component(immediate = true) -public class OLT { - +public class OLT implements AccessDeviceService { private final Logger log = getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @@ -65,10 +73,14 @@ public class OLT { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry networkConfig; + private final DeviceListener deviceListener = new InternalDeviceListener(); private ApplicationId appId; + private static final VlanId DEFAULT_VLAN = VlanId.vlanId((short) 0); public static final int OFFSET = 200; public static final int UPLINK_PORT = 129; @@ -94,11 +106,39 @@ public class OLT { label = "The gfast device id") private String gfastDevice = GFAST_DEVICE; + private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>(); + + private InternalNetworkConfigListener configListener = + new InternalNetworkConfigListener(); + private static final Class<AccessDeviceConfig> CONFIG_CLASS = + AccessDeviceConfig.class; + + private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory = + new ConfigFactory<DeviceId, AccessDeviceConfig>( + SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") { + @Override + public AccessDeviceConfig createConfig() { + return new AccessDeviceConfig(); + } + }; @Activate public void activate() { appId = coreService.registerApplication("org.onosproject.olt"); + networkConfig.registerConfigFactory(configFactory); + networkConfig.addListener(configListener); + + networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach( + subject -> { + AccessDeviceConfig config = networkConfig.getConfig(subject, AccessDeviceConfig.class); + if (config != null) { + AccessDeviceData data = config.getOlt(); + oltData.put(data.deviceId(), data); + } + } + ); + /*deviceService.addListener(deviceListener); deviceService.getPorts(DeviceId.deviceId(oltDevice)).stream().forEach( @@ -129,6 +169,8 @@ public class OLT { @Deactivate public void deactivate() { + networkConfig.removeListener(configListener); + networkConfig.unregisterConfigFactory(configFactory); log.info("Stopped"); } @@ -136,16 +178,13 @@ public class OLT { public void modified(ComponentContext context) { Dictionary<?, ?> properties = context.getProperties(); - String s = Tools.get(properties, "uplinkPort"); uplinkPort = Strings.isNullOrEmpty(s) ? UPLINK_PORT : Integer.parseInt(s); s = Tools.get(properties, "oltDevice"); oltDevice = Strings.isNullOrEmpty(s) ? OLT_DEVICE : s; - } - private short fetchVlanId(PortNumber port) { long p = port.toLong() + OFFSET; if (p > 4095) { @@ -155,7 +194,6 @@ public class OLT { return (short) p; } - private void provisionVlanOnPort(String deviceId, int uplinkPort, PortNumber p, short vlanId) { DeviceId did = DeviceId.deviceId(deviceId); @@ -198,7 +236,73 @@ public class OLT { flowObjectiveService.forward(did, upFwd); flowObjectiveService.forward(did, downFwd); + } + + @Override + public void provisionSubscriber(ConnectPoint port, VlanId vlan) { + AccessDeviceData olt = oltData.get(port.deviceId()); + + if (olt == null) { + log.warn("No data found for OLT device {}", port.deviceId()); + return; + } + + provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan()); + } + + private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort, + PortNumber subscriberPort, + VlanId subscriberVlan, VlanId deviceVlan) { + + TrafficSelector upstream = DefaultTrafficSelector.builder() + .matchVlanId(DEFAULT_VLAN) + .matchInPort(subscriberPort) + .build(); + + TrafficSelector downstream = DefaultTrafficSelector.builder() + .matchVlanId(deviceVlan) + .matchInPort(uplinkPort) + .build(); + + TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder() + .setVlanId(subscriberVlan) + .pushVlan() + .setVlanId(deviceVlan) + .setOutput(uplinkPort) + .build(); + TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder() + .popVlan() + .setVlanId(DEFAULT_VLAN) + .setOutput(subscriberPort) + .build(); + + + ForwardingObjective upFwd = DefaultForwardingObjective.builder() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1000) + .makePermanent() + .withSelector(upstream) + .fromApp(appId) + .withTreatment(upstreamTreatment) + .add(); + + ForwardingObjective downFwd = DefaultForwardingObjective.builder() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1000) + .makePermanent() + .withSelector(downstream) + .fromApp(appId) + .withTreatment(downstreamTreatment) + .add(); + + flowObjectiveService.forward(deviceId, upFwd); + flowObjectiveService.forward(deviceId, downFwd); + } + + @Override + public void removeSubscriber(ConnectPoint port) { + throw new UnsupportedOperationException("Not yet implemented"); } private class InternalDeviceListener implements DeviceListener { @@ -226,7 +330,27 @@ public class OLT { } } + private class InternalNetworkConfigListener implements NetworkConfigListener { + @Override + public void event(NetworkConfigEvent event) { + switch (event.type()) { -} - + case CONFIG_ADDED: + case CONFIG_UPDATED: + if (event.configClass().equals(CONFIG_CLASS)) { + AccessDeviceConfig config = + networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS); + if (config != null) { + oltData.put(config.getOlt().deviceId(), config.getOlt()); + } + } + break; + case CONFIG_UNREGISTERED: + case CONFIG_REMOVED: + default: + break; + } + } + } +} diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java new file mode 100644 index 00000000..d9b4559b --- /dev/null +++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java @@ -0,0 +1,58 @@ +/* + * 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.olt; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.VlanId; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; + +/** + * Adds a subscriber to an access device. + */ +@Command(scope = "onos", name = "add-subscriber-access", + description = "Adds a subscriber to an access device") +public class SubscriberAddCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "deviceId", description = "Access device ID", + required = true, multiValued = false) + private String strDeviceId = null; + + @Argument(index = 1, name = "port", description = "Subscriber port number", + required = true, multiValued = false) + private String strPort = null; + + @Argument(index = 2, name = "vlanId", + description = "VLAN ID to add", + required = true, multiValued = false) + private String strVlanId = null; + + @Override + protected void execute() { + AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class); + + DeviceId deviceId = DeviceId.deviceId(strDeviceId); + PortNumber port = PortNumber.portNumber(strPort); + VlanId vlan = VlanId.vlanId(Short.parseShort(strVlanId)); + ConnectPoint connectPoint = new ConnectPoint(deviceId, port); + + service.provisionSubscriber(connectPoint, vlan); + } +} diff --git a/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..00ebe9d1 --- /dev/null +++ b/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,29 @@ +<!-- + ~ 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. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.olt.SubscriberAddCommand"/> + <completers> + <ref component-id="deviceIdCompleter"/> + <null/> + </completers> + </command> + </command-bundle> + + <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/> +</blueprint> diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java index 8b447af4..40ee55fc 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java @@ -93,7 +93,7 @@ public class DefaultRoutingHandler { try { populationStatus = Status.STARTED; rulePopulator.resetCounter(); - log.info("Starts to populate routing rules"); + log.info("Starting to populate segment-routing rules"); log.debug("populateAllRoutingRules: populationStatus is STARTED"); for (Device sw : srManager.deviceService.getDevices()) { @@ -117,7 +117,7 @@ public class DefaultRoutingHandler { log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED"); populationStatus = Status.SUCCEEDED; - log.info("Completes routing rule population. Total # of rules pushed : {}", + log.info("Completed routing rule population. Total # of rules pushed : {}", rulePopulator.getCounter()); return true; } finally { @@ -426,7 +426,7 @@ public class DefaultRoutingHandler { .get(itrIdx); for (DeviceId targetSw : swViaMap.keySet()) { Set<DeviceId> nextHops = new HashSet<>(); - + log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw); for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) { if (via.isEmpty()) { nextHops.add(destSw); @@ -456,7 +456,7 @@ public class DefaultRoutingHandler { // rule for both subnet and router IP. if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) { List<Ip4Prefix> subnets = config.getSubnets(destSw); - log.debug("populateEcmpRoutingRulePartial in device {} towards {} for subnets {}", + log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}", targetSw, destSw, subnets); result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, @@ -468,7 +468,7 @@ public class DefaultRoutingHandler { Ip4Address routerIp = config.getRouterIp(destSw); IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH); - log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}", + log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}", targetSw, destSw, routerIpPrefix); result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops); if (!result) { @@ -479,7 +479,7 @@ public class DefaultRoutingHandler { } else if (config.isEdgeDevice(targetSw)) { Ip4Address routerIp = config.getRouterIp(destSw); IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH); - log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}", + log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}", targetSw, destSw, routerIpPrefix); result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops); if (!result) { @@ -488,7 +488,7 @@ public class DefaultRoutingHandler { } // Populates MPLS rules to all routers - log.debug("populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules", + log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules", targetSw, destSw); result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops); if (!result) { @@ -500,7 +500,7 @@ public class DefaultRoutingHandler { /** * Populates table miss entries for all tables, and pipeline rules for VLAN - * and TACM tables. + * and TCAM tables. XXX rename/rethink * * @param deviceId Switch ID to set the rules */ @@ -534,6 +534,8 @@ public class DefaultRoutingHandler { /** * Resume the flow rule population process if it was aborted for any reason. * Mostly the process is aborted when the groups required are not set yet. + * XXX is this called? + * */ public void resumePopulationProcess() { statusLock.lock(); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java index eef1b147..0bc155b8 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java @@ -134,7 +134,7 @@ public class DeviceConfiguration implements DeviceProperties { @Override public int getSegmentId(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("getSegmentId for device{} is {}", + log.trace("getSegmentId for device{} is {}", deviceId, deviceConfigMap.get(deviceId).nodeSid); return deviceConfigMap.get(deviceId).nodeSid; @@ -189,7 +189,7 @@ public class DeviceConfiguration implements DeviceProperties { @Override public MacAddress getDeviceMac(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("getDeviceMac for device{} is {}", + log.trace("getDeviceMac for device{} is {}", deviceId, deviceConfigMap.get(deviceId).mac); return deviceConfigMap.get(deviceId).mac; @@ -209,7 +209,7 @@ public class DeviceConfiguration implements DeviceProperties { */ public Ip4Address getRouterIp(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("getDeviceIp for device{} is {}", + log.trace("getDeviceIp for device{} is {}", deviceId, deviceConfigMap.get(deviceId).ip); return deviceConfigMap.get(deviceId).ip; @@ -231,7 +231,7 @@ public class DeviceConfiguration implements DeviceProperties { @Override public boolean isEdgeDevice(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("isEdgeDevice for device{} is {}", + log.trace("isEdgeDevice for device{} is {}", deviceId, deviceConfigMap.get(deviceId).isEdge); return deviceConfigMap.get(deviceId).isEdge; @@ -297,7 +297,7 @@ public class DeviceConfiguration implements DeviceProperties { */ public List<Ip4Address> getSubnetGatewayIps(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("getSubnetGatewayIps for device{} is {}", + log.trace("getSubnetGatewayIps for device{} is {}", deviceId, deviceConfigMap.get(deviceId).gatewayIps.values()); return new ArrayList<>(deviceConfigMap.get(deviceId).gatewayIps.values()); @@ -314,7 +314,7 @@ public class DeviceConfiguration implements DeviceProperties { */ public List<Ip4Prefix> getSubnets(DeviceId deviceId) { if (deviceConfigMap.get(deviceId) != null) { - log.debug("getSubnets for device{} is {}", + log.trace("getSubnets for device{} is {}", deviceId, deviceConfigMap.get(deviceId).subnets.values()); return new ArrayList<>(deviceConfigMap.get(deviceId).subnets.values()); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java index 2e2041c4..e9a59bae 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java @@ -31,7 +31,7 @@ import java.util.List; /** * This class creates bandwidth constrained breadth first tree and returns paths - * from root Device to leaf Devicees which satisfies the bandwidth condition. If + * from root Device to leaf Devices which satisfies the bandwidth condition. If * bandwidth parameter is not specified, the normal breadth first tree will be * calculated. The paths are snapshot paths at the point of the class * instantiation. @@ -281,7 +281,7 @@ public class ECMPShortestPathGraph { * Return the complete info of the computed ECMP paths for each Device * learned in multiple iterations from the root Device. * - * @return the hash table of Devicees learned in multiple Dijkstra + * @return the hash table of Devices learned in multiple Dijkstra * iterations and corresponding ECMP paths to it from the root * Device */ @@ -305,8 +305,8 @@ public class ECMPShortestPathGraph { * Return the complete info of the computed ECMP paths for each Device * learned in multiple iterations from the root Device. * - * @return the hash table of Devicees learned in multiple Dijkstra - * iterations and corresponding ECMP paths in terms of Devicees to + * @return the hash table of Devices learned in multiple Dijkstra + * iterations and corresponding ECMP paths in terms of Devices to * be traversed to it from the root Device */ public HashMap<Integer, HashMap<DeviceId, diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java index e37fe52a..14ce679b 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java @@ -115,7 +115,7 @@ public class IpHandler { /** * Forwards IP packets in the buffer to the destination IP address. * It is called when the controller finds the destination MAC address - * via ARP responsees. + * via ARP responses. * * @param deviceId switch device ID * @param destIpAddress destination IP address diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java index d802be91..7641571d 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java @@ -150,7 +150,7 @@ public class RoutingRulePopulator { /** * Populates IP flow rules for the router IP address. * - * @param deviceId device ID to set the rules + * @param deviceId target device ID to set the rules * @param ipPrefix the IP address of the destination router * @param destSw device ID of the destination router * @param nextHops next hop switch ID list @@ -210,9 +210,9 @@ public class RoutingRulePopulator { } /** - * Populates MPLS flow rules to all transit routers. + * Populates MPLS flow rules to all routers. * - * @param deviceId device ID of the switch to set the rules + * @param deviceId target device ID of the switch to set the rules * @param destSwId destination switch device ID * @param nextHops next hops switch ID list * @return true if all rules are set successfully, false otherwise @@ -379,7 +379,7 @@ public class RoutingRulePopulator { .addCondition(Criteria.matchEthDst(config .getDeviceMac(deviceId))); fob.permit().fromApp(srManager.appId); - log.debug("populateTableVlan: Installing filtering objective for router mac"); + log.debug("populateTableTMac: Installing filtering objective for router mac"); srManager.flowObjectiveService. filter(deviceId, fob.add(new SRObjectiveContext(deviceId, diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index 9011160c..eb2cf569 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -296,7 +296,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { } /** - * Returns the GrouopKey object for the device and the NighborSet given. + * Returns the GroupKey object for the device and the NeighborSet given. + * XXX is this called * * @param ns NeightborSet object for the GroupKey * @return GroupKey object for the NeighborSet diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java index 3dc312df..c960adca 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java @@ -35,13 +35,14 @@ import org.onosproject.store.service.EventuallyConsistentMap; * where D0 and D3 are edge devices and D1 and D2 are transit devices. * Assume device D0 is connected to 2 neighbors (D1 and D2 ). * The following groups will be created in D0: - * 1) all ports to D1 + with no label push, - * 2) all ports to D1 + with label 102 pushed, - * 3) all ports to D1 + with label 103 pushed, + * 1) all ports to D1 + with no label push, // direct attach case, seen + * 2) all ports to D1 + with label 102 pushed, // this is not needed + * 3) all ports to D1 + with label 103 pushed, // maybe needed, sometimes seen * 4) all ports to D2 + with no label push, * 5) all ports to D2 + with label 101 pushed, * 6) all ports to D2 + with label 103 pushed, - * 7) all ports to D1 and D2 + with label 103 pushed + * 7) all ports to D1 and D2 + with label 103 pushed // ecmp case + * 8) what about ecmp no label case */ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java index 05ebccf9..0a3ea2cd 100644 --- a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java @@ -18,6 +18,7 @@ package org.onosproject.vtnrsc.virtualport; import java.util.Collection; import org.onosproject.net.DeviceId; +import org.onosproject.vtnrsc.FixedIp; import org.onosproject.vtnrsc.TenantId; import org.onosproject.vtnrsc.TenantNetworkId; import org.onosproject.vtnrsc.VirtualPort; @@ -44,6 +45,14 @@ public interface VirtualPortService { VirtualPort getPort(VirtualPortId virtualPortId); /** + * Returns the virtualPort associated with the fixedIP. + * + * @param fixedIP the fixedIP identifier + * @return virtualPort. + */ + VirtualPort getPort(FixedIp fixedIP); + + /** * Returns the collection of the currently known virtualPort. * @return collection of VirtualPort. */ diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java index 926809c9..c45373b9 100644 --- a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java @@ -17,9 +17,12 @@ package org.onosproject.vtnrsc.virtualport.impl; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -68,6 +71,7 @@ public class VirtualPortManager implements VirtualPortService { private static final String TENANTID_NOT_NULL = "TenantId cannot be null"; private static final String NETWORKID_NOT_NULL = "NetworkId cannot be null"; private static final String DEVICEID_NOT_NULL = "DeviceId cannot be null"; + private static final String FIXEDIP_NOT_NULL = "FixedIp cannot be null"; protected Map<VirtualPortId, VirtualPort> vPortStore; protected ApplicationId appId; @@ -125,6 +129,25 @@ public class VirtualPortManager implements VirtualPortService { } @Override + public VirtualPort getPort(FixedIp fixedIP) { + checkNotNull(fixedIP, FIXEDIP_NOT_NULL); + List<VirtualPort> vPorts = new ArrayList<>(); + vPortStore.values().stream().forEach(p -> { + Iterator<FixedIp> fixedIps = p.fixedIps().iterator(); + while (fixedIps.hasNext()) { + if (fixedIps.next().equals(fixedIP)) { + vPorts.add(p); + break; + } + } + }); + if (vPorts.size() == 0) { + return null; + } + return vPorts.get(0); + } + + @Override public Collection<VirtualPort> getPorts() { return Collections.unmodifiableCollection(vPortStore.values()); } |