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