blob: 43e7c4df738d57a90dfb3868cacc130b9e5fffb2 [file] [log] [blame]
Shubham Sharma1ad16632019-11-26 11:09:21 +00001/*
2 * Copyright 2015-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.opencord.aaa.impl;
17
18import com.google.common.base.Charsets;
19import org.junit.After;
20import org.junit.Before;
21import org.junit.Test;
22import org.onlab.junit.TestUtils;
23import org.onlab.packet.BasePacket;
24import org.onlab.packet.DeserializationException;
25import org.onlab.packet.EAP;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.RADIUS;
29import org.onlab.packet.RADIUSAttribute;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreServiceAdapter;
32import org.onosproject.event.DefaultEventSinkRegistry;
33import org.onosproject.event.Event;
34import org.onosproject.event.EventDeliveryService;
35import org.onosproject.event.EventSink;
36import org.onosproject.net.config.Config;
37import org.onosproject.net.config.NetworkConfigRegistryAdapter;
38import org.onosproject.net.packet.DefaultInboundPacket;
39import org.onosproject.net.packet.InboundPacket;
40import org.onosproject.net.packet.PacketContext;
41import org.onosproject.net.packet.PacketService;
42import org.opencord.aaa.AaaConfig;
43import org.slf4j.Logger;
44
45import java.lang.reflect.Field;
46import java.net.InetAddress;
47import java.net.UnknownHostException;
48import java.nio.ByteBuffer;
49
50import static com.google.common.base.Preconditions.checkState;
51import static org.hamcrest.Matchers.is;
52import static org.hamcrest.Matchers.notNullValue;
53import static org.junit.Assert.assertThat;
54import static org.onosproject.net.NetTestTools.connectPoint;
55import static org.slf4j.LoggerFactory.getLogger;
56
57/**
58 * Set of tests of the ONOS application component for AAA Statistics.
59 */
60public class AaaStatisticsTest extends AaaTestBase {
61
62 static final String BAD_IP_ADDRESS = "198.51.100.0";
63
64 private final Logger log = getLogger(getClass());
65 private AaaManager aaaManager;
66 private AaaStatisticsManager aaaStatisticsManager;
Kartikey Dubeybe14f472019-10-01 12:18:35 +000067 private AaaSupplicantMachineStatsManager aaaSupplicantStatsManager;
Shubham Sharma1ad16632019-11-26 11:09:21 +000068
69 class AaaManagerWithoutRadiusServer extends AaaManager {
70 protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
71 super.sendRadiusPacket(radiusPacket, inPkt);
72 aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
73 savePacket(radiusPacket);
74 }
75
76 // changed the configuration of parent method to protected
77 protected void configureRadiusCommunication() {
78 PacketService pktService = new MockPacketService();
79 ApplicationId appId = new CoreServiceAdapter().registerApplication("org.opencord.aaa");
80 aaaManager.impl = new TestSocketBasedRadiusCommunicator(appId, pktService, aaaManager);
81 }
82 }
83
84 /**
85 * Mocks the AAAConfig class to force usage of an unroutable address for the
86 * RADIUS server.
87 */
88 static class MockAaaConfig extends AaaConfig {
89 @Override
90 public InetAddress radiusIp() {
91 try {
92 return InetAddress.getByName(BAD_IP_ADDRESS);
93 } catch (UnknownHostException ex) {
94 throw new IllegalStateException(ex);
95 }
96 }
97 }
98
99 /**
100 * Mocks the network config registry.
101 */
102 @SuppressWarnings("unchecked")
103 private static final class TestNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
104 @Override
105 public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
106 AaaConfig aaaConfig = new MockAaaConfig();
107 return (C) aaaConfig;
108 }
109 }
110
111 public static class TestEventDispatcher extends DefaultEventSinkRegistry implements EventDeliveryService {
112
113 @Override
114 @SuppressWarnings("unchecked")
115 public synchronized void post(Event event) {
116 EventSink sink = getSink(event.getClass());
117 checkState(sink != null, "No sink for event %s", event);
118 sink.process(event);
119 }
120
121 @Override
122 public void setDispatchTimeLimit(long millis) {
123 }
124
125 @Override
126 public long getDispatchTimeLimit() {
127 return 0;
128 }
129 }
130
131 /**
132 * Constructs an Ethernet packet containing a RADIUS challenge packet.
133 *
134 * @param challengeCode
135 * code to use in challenge packet
136 * @param challengeType
137 * type to use in challenge packet
138 * @return Ethernet packet
139 */
140 private RADIUS constructRadiusCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
141
Shubham Sharma048cc262019-06-19 14:18:50 +0000142 String challenge = "12345678901234567";
Shubham Sharma1ad16632019-11-26 11:09:21 +0000143
Shubham Sharma048cc262019-06-19 14:18:50 +0000144 EAP eap = new EAP(challengeType, (byte) 4, challengeType,
145 challenge.getBytes(Charsets.US_ASCII));
146 eap.setIdentifier((byte) 4);
Shubham Sharma1ad16632019-11-26 11:09:21 +0000147
Shubham Sharma048cc262019-06-19 14:18:50 +0000148 RADIUS radius = new RADIUS();
149 radius.setCode(challengeCode);
150 radius.setIdentifier((byte) 4);
151 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
152 challenge.getBytes(Charsets.US_ASCII));
Shubham Sharma1ad16632019-11-26 11:09:21 +0000153
Shubham Sharma048cc262019-06-19 14:18:50 +0000154 radius.setPayload(eap);
155 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
156 eap.serialize());
157 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH,
158 aaaManager.radiusSecret.getBytes());
159 return radius;
Shubham Sharma1ad16632019-11-26 11:09:21 +0000160 }
161
162 public static void injectEventDispatcher(Object manager, EventDeliveryService svc) {
163 Class mc = manager.getClass();
164 for (Field f : mc.getSuperclass().getDeclaredFields()) {
165 if (f.getType().equals(EventDeliveryService.class)) {
166 try {
167 TestUtils.setField(manager, f.getName(), svc);
168 } catch (TestUtils.TestUtilsException e) {
169 throw new IllegalArgumentException("Unable to inject reference", e);
170 }
171 break;
172 }
173 }
174 }
175
176/**
177 * Set up the services required by the AAA application.
178 */
179 @Before
180 public void setUp() {
181 aaaManager = new AaaManagerWithoutRadiusServer();
Shubham Sharma048cc262019-06-19 14:18:50 +0000182 aaaManager.radiusOperationalStatusService = new RadiusOperationalStatusManager();
Shubham Sharma1ad16632019-11-26 11:09:21 +0000183 aaaManager.netCfgService = new TestNetworkConfigRegistry();
184 aaaManager.coreService = new CoreServiceAdapter();
185 aaaManager.packetService = new MockPacketService();
186 aaaManager.deviceService = new TestDeviceService();
187 aaaManager.sadisService = new MockSadisService();
188 aaaManager.cfgService = new MockCfgService();
189 aaaStatisticsManager = new AaaStatisticsManager();
Kartikey Dubeybe14f472019-10-01 12:18:35 +0000190 aaaSupplicantStatsManager = new AaaSupplicantMachineStatsManager();
Shubham Sharma1ad16632019-11-26 11:09:21 +0000191 TestUtils.setField(aaaStatisticsManager, "eventDispatcher", new TestEventDispatcher());
192 aaaStatisticsManager.activate();
Kartikey Dubeybe14f472019-10-01 12:18:35 +0000193 TestUtils.setField(aaaSupplicantStatsManager, "eventDispatcher", new TestEventDispatcher());
194 aaaSupplicantStatsManager.activate();
Shubham Sharma1ad16632019-11-26 11:09:21 +0000195 aaaManager.aaaStatisticsManager = this.aaaStatisticsManager;
Kartikey Dubeybe14f472019-10-01 12:18:35 +0000196 aaaManager.aaaSupplicantStatsManager = this.aaaSupplicantStatsManager;
Shubham Sharma1ad16632019-11-26 11:09:21 +0000197 TestUtils.setField(aaaManager, "eventDispatcher", new TestEventDispatcher());
198 aaaManager.activate(new AaaTestBase.MockComponentContext());
199 }
200
201/**
202 * Tear down the AAA application.
203 */
204@After
205public void tearDown() {
206 aaaManager.deactivate(new AaaTestBase.MockComponentContext());
207}
208
209/**
210 * Extracts the RADIUS packet from a packet sent by the supplicant.
211 *
212 * @param radius
213 * RADIUS packet sent by the supplicant
214 * @throws DeserializationException
215 * if deserialization of the packet contents fails.
216 */
217private void checkRadiusPacketFromSupplicant(RADIUS radius) throws DeserializationException {
218 assertThat(radius, notNullValue());
219 EAP eap = radius.decapsulateMessage();
220 assertThat(eap, notNullValue());
221}
222
223/**
224 * Fetches the sent packet at the given index. The requested packet must be the
225 * last packet on the list.
226 *
227 * @param index
228 * index into sent packets array
229 * @return packet
230 */
231private BasePacket fetchPacket(int index) {
232 BasePacket packet = savedPackets.get(index);
233 assertThat(packet, notNullValue());
234 return packet;
235}
236
237 /** Tests the authentication path through the AAA application.
238 * And counts the aaa Stats for successful transmission.
239 * @throws DeserializationException
240 * if packed deserialization fails.
241 */
242 @Test
243 public void testAaaStatisticsForAcceptedPackets() throws Exception {
244
245 // (1) Supplicant start up
246 Ethernet startPacket = constructSupplicantStartPacket();
247 sendPacket(startPacket);
248
249 Ethernet responsePacket = (Ethernet) fetchPacket(0);
250 checkRadiusPacket(aaaManager, responsePacket, EAP.ATTR_IDENTITY);
251
252 // (2) Supplicant identify
253
254 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
255 sendPacket(identifyPacket);
256
257 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
258 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
259
260 assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
261 assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
262 is("testuser"));
263 IpAddress nasIp = IpAddress.valueOf(IpAddress.Version.INET,
264 radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP).getValue());
265 assertThat(nasIp.toString(), is(aaaManager.nasIpAddress.getHostAddress()));
266
267 // State machine should have been created by now
268
269 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(SESSION_ID);
270 assertThat(stateMachine, notNullValue());
271 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
272
273 // (3) RADIUS MD5 challenge
274
275 RADIUS radiusCodeAccessChallengePacket = constructRadiusCodeAccessChallengePacket(
276 RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
277 aaaManager.handleRadiusPacket(radiusCodeAccessChallengePacket);
278
279 Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
280 checkRadiusPacket(aaaManager, radiusChallengeMD5Packet, EAP.ATTR_MD5);
281
282 // (4) Supplicant MD5 response
283
284 Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(stateMachine, EAP.ATTR_MD5,
285 stateMachine.challengeIdentifier(), radiusChallengeMD5Packet);
286 sendPacket(md5RadiusPacket);
287
288 RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
289
290 checkRadiusPacketFromSupplicant(responseMd5RadiusPacket);
Shubham Sharma048cc262019-06-19 14:18:50 +0000291 assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 9));
Shubham Sharma1ad16632019-11-26 11:09:21 +0000292 assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
293
294 // State machine should be in pending state
295
296 assertThat(stateMachine, notNullValue());
297 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
298
299 // (5) RADIUS Success
300
301 RADIUS successPacket =
302 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
303 aaaManager.handleRadiusPacket((successPacket));
304 Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
305
306 checkRadiusPacket(aaaManager, supplicantSuccessPacket, EAP.SUCCESS);
307
308 // State machine should be in authorized state
309
310 assertThat(stateMachine, notNullValue());
311 assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
312
313 // Counts the aaa Statistics count and displays in the log
314 countAaaStatistics();
315
316 }
317
318 /** Tests the count for defected packets.
319 *
320 * @throws DeserializationException
321 * if packed deserialization fails.
322 */
323 @Test
324 public void testAaaStatisticsForDefectivePackets() throws Exception {
325 // (1) Supplicant start up
326 Ethernet startPacket = constructSupplicantStartPacket();
327 sendPacket(startPacket);
328
329 // (2) Supplicant identify
330
331 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
332 sendPacket(identifyPacket);
333
334 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
335
336 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
337
338 // Calling the mock test socket based to handle packet
339 aaaManager.impl.handlePacketFromServer(null);
340 // State machine should have been created by now
341
342 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(SESSION_ID);
343
344 // (3) RADIUS MD5 challenge
345
346 RADIUS radiusCodeAccessChallengePacket = constructRadiusCodeAccessChallengePacket(
347 RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
348 aaaManager.handleRadiusPacket(radiusCodeAccessChallengePacket);
349
350 Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
351
352 // (4) Supplicant MD5 response
353
354 Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(stateMachine, EAP.ATTR_MD5,
355 stateMachine.challengeIdentifier(), radiusChallengeMD5Packet);
356 sendPacket(md5RadiusPacket);
357 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
358 // (5) RADIUS Rejected
359
360 RADIUS rejectedPacket =
361 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_REJECT, EAP.FAILURE);
362 aaaManager.handleRadiusPacket((rejectedPacket));
363 Ethernet supplicantRejectedPacket = (Ethernet) fetchPacket(4);
364
365 checkRadiusPacket(aaaManager, supplicantRejectedPacket, EAP.FAILURE);
366
367 // State machine should be in unauthorized state
368 assertThat(stateMachine, notNullValue());
369 assertThat(stateMachine.state(), is(StateMachine.STATE_UNAUTHORIZED));
370 // Calculated the total round trip time
371 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
372 // Counts the aaa Statistics count and displays in the log
373 countAaaStatistics();
374
375 }
376
377 /*
378 * Tests the retransmitted packet and malformed packet count
379 *
380 * @throws DeserializationException
381 * if packed deserialization fails.
382 */
383 @Test
384 public void testRequestRetransmittedCount() throws Exception {
385
386 // (1) Supplicant start up
387 Ethernet startPacket = constructSupplicantStartPacket();
388 sendPacket(startPacket);
389
390 // (2) Supplicant identify
391
392 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
393 sendPacket(identifyPacket);
394
395 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
396 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
397
398 // again creating pending state for same packet
399 constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
400 sendPacket(identifyPacket);
401 aaaManager.impl.handlePacketFromServer(null);
402 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
403
404 // creating malformed packet
405 final ByteBuffer byteBuffer = ByteBuffer.wrap(startPacket.serialize());
406 InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
407 startPacket, byteBuffer);
408
409 PacketContext context = new TestPacketContext(127L, inPacket, null, false);
410 aaaManager.impl.handlePacketFromServer(context);
411 countAaaStatistics();
412
413 }
414
415 // Calculates the AAA statistics count.
416 public void countAaaStatistics() {
417 assertThat(aaaStatisticsManager.getAaaStats().getAcceptResponsesRx(), notNullValue());
418 assertThat(aaaStatisticsManager.getAaaStats().getAccessRequestsTx(), notNullValue());
419 assertThat(aaaStatisticsManager.getAaaStats().getChallengeResponsesRx(), notNullValue());
420 assertThat(aaaStatisticsManager.getAaaStats().getDroppedResponsesRx(), notNullValue());
421 assertThat(aaaStatisticsManager.getAaaStats().getInvalidValidatorsRx(), notNullValue());
422 assertThat(aaaStatisticsManager.getAaaStats().getMalformedResponsesRx(), notNullValue());
423 assertThat(aaaStatisticsManager.getAaaStats().getPendingRequests(), notNullValue());
424 assertThat(aaaStatisticsManager.getAaaStats().getRejectResponsesRx(), notNullValue());
425 assertThat(aaaStatisticsManager.getAaaStats().getRequestReTx(), notNullValue());
426 assertThat(aaaStatisticsManager.getAaaStats().getRequestRttMilis(), notNullValue());
427 assertThat(aaaStatisticsManager.getAaaStats().getUnknownServerRx(), notNullValue());
428 assertThat(aaaStatisticsManager.getAaaStats().getUnknownTypeRx(), notNullValue());
429 }
430
431 /*
432 * Mock implementation of SocketBasedRadiusCommunicator class.
433 *
434 */
435 class TestSocketBasedRadiusCommunicator extends SocketBasedRadiusCommunicator {
436
437 TestSocketBasedRadiusCommunicator(ApplicationId appId, PacketService pktService, AaaManager aaaManager) {
438 super(appId, pktService, aaaManager);
439 }
440
441 // Implementation of socketBasedRadiusCommunicator--> run() method
442 public void handlePacketFromServer(PacketContext context) {
443
444 RADIUS incomingPkt = (RADIUS) fetchPacket(savedPackets.size() - 1);
445 try {
446 if (context == null) {
447 aaaStatisticsManager.handleRoundtripTime(incomingPkt.getIdentifier());
448 aaaManager.handleRadiusPacket(incomingPkt);
449 } else if (null != context) {
450 aaaManager.checkForPacketFromUnknownServer("100.100.100.0");
451 aaaStatisticsManager.handleRoundtripTime(incomingPkt.getIdentifier());
452 aaaManager.handleRadiusPacket(incomingPkt);
453 incomingPkt =
454 RADIUS.deserializer().deserialize(incomingPkt.generateAuthCode(), 0, 1);
455 }
456 } catch (DeserializationException dex) {
457 aaaManager.aaaStatisticsManager.getAaaStats().increaseMalformedResponsesRx();
458 aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
459 log.error("Cannot deserialize packet", dex);
460 } catch (StateMachineException sme) {
461 log.error("Illegal state machine operation", sme);
462 }
463
464 }
465
466 }
467
Kartikey Dubeybe14f472019-10-01 12:18:35 +0000468}