Ray Milkey
Committed by Gerrit Code Review

Support RADIUS server outside of the ONOS network

Change-Id: I7e64faae6831467e084db878e02023d40fb33f07
......@@ -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";
......
/*
* 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));
}
}
/*
* 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));
}
}
......@@ -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 {
......