blob: e4718b7e7afd0a74f3bce6b391e6ffd38ee051c2 [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
141 String challenge = "12345678901234567";
142 EAP eap = new EAP(challengeType, (byte) 1, challengeType, challenge.getBytes(Charsets.US_ASCII));
143 eap.setIdentifier((byte) 1);
144
145 RADIUS radius = new RADIUS();
146 radius.setCode(challengeCode);
147
148 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, challenge.getBytes(Charsets.US_ASCII));
149
150 radius.setPayload(eap);
151 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE, eap.serialize());
152 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, aaaManager.radiusSecret.getBytes());
153 return radius;
154
155 }
156
157 public static void injectEventDispatcher(Object manager, EventDeliveryService svc) {
158 Class mc = manager.getClass();
159 for (Field f : mc.getSuperclass().getDeclaredFields()) {
160 if (f.getType().equals(EventDeliveryService.class)) {
161 try {
162 TestUtils.setField(manager, f.getName(), svc);
163 } catch (TestUtils.TestUtilsException e) {
164 throw new IllegalArgumentException("Unable to inject reference", e);
165 }
166 break;
167 }
168 }
169 }
170
171/**
172 * Set up the services required by the AAA application.
173 */
174 @Before
175 public void setUp() {
176 aaaManager = new AaaManagerWithoutRadiusServer();
177 aaaManager.netCfgService = new TestNetworkConfigRegistry();
178 aaaManager.coreService = new CoreServiceAdapter();
179 aaaManager.packetService = new MockPacketService();
180 aaaManager.deviceService = new TestDeviceService();
181 aaaManager.sadisService = new MockSadisService();
182 aaaManager.cfgService = new MockCfgService();
183 aaaStatisticsManager = new AaaStatisticsManager();
184 TestUtils.setField(aaaStatisticsManager, "eventDispatcher", new TestEventDispatcher());
185 aaaStatisticsManager.activate();
186 aaaManager.aaaStatisticsManager = this.aaaStatisticsManager;
187 TestUtils.setField(aaaManager, "eventDispatcher", new TestEventDispatcher());
188 aaaManager.activate(new AaaTestBase.MockComponentContext());
189 }
190
191/**
192 * Tear down the AAA application.
193 */
194@After
195public void tearDown() {
196 aaaManager.deactivate(new AaaTestBase.MockComponentContext());
197}
198
199/**
200 * Extracts the RADIUS packet from a packet sent by the supplicant.
201 *
202 * @param radius
203 * RADIUS packet sent by the supplicant
204 * @throws DeserializationException
205 * if deserialization of the packet contents fails.
206 */
207private void checkRadiusPacketFromSupplicant(RADIUS radius) throws DeserializationException {
208 assertThat(radius, notNullValue());
209 EAP eap = radius.decapsulateMessage();
210 assertThat(eap, notNullValue());
211}
212
213/**
214 * Fetches the sent packet at the given index. The requested packet must be the
215 * last packet on the list.
216 *
217 * @param index
218 * index into sent packets array
219 * @return packet
220 */
221private BasePacket fetchPacket(int index) {
222 BasePacket packet = savedPackets.get(index);
223 assertThat(packet, notNullValue());
224 return packet;
225}
226
227 /** Tests the authentication path through the AAA application.
228 * And counts the aaa Stats for successful transmission.
229 * @throws DeserializationException
230 * if packed deserialization fails.
231 */
232 @Test
233 public void testAaaStatisticsForAcceptedPackets() throws Exception {
234
235 // (1) Supplicant start up
236 Ethernet startPacket = constructSupplicantStartPacket();
237 sendPacket(startPacket);
238
239 Ethernet responsePacket = (Ethernet) fetchPacket(0);
240 checkRadiusPacket(aaaManager, responsePacket, EAP.ATTR_IDENTITY);
241
242 // (2) Supplicant identify
243
244 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
245 sendPacket(identifyPacket);
246
247 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
248 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
249
250 assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
251 assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
252 is("testuser"));
253 IpAddress nasIp = IpAddress.valueOf(IpAddress.Version.INET,
254 radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP).getValue());
255 assertThat(nasIp.toString(), is(aaaManager.nasIpAddress.getHostAddress()));
256
257 // State machine should have been created by now
258
259 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(SESSION_ID);
260 assertThat(stateMachine, notNullValue());
261 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
262
263 // (3) RADIUS MD5 challenge
264
265 RADIUS radiusCodeAccessChallengePacket = constructRadiusCodeAccessChallengePacket(
266 RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
267 aaaManager.handleRadiusPacket(radiusCodeAccessChallengePacket);
268
269 Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
270 checkRadiusPacket(aaaManager, radiusChallengeMD5Packet, EAP.ATTR_MD5);
271
272 // (4) Supplicant MD5 response
273
274 Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(stateMachine, EAP.ATTR_MD5,
275 stateMachine.challengeIdentifier(), radiusChallengeMD5Packet);
276 sendPacket(md5RadiusPacket);
277
278 RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
279
280 checkRadiusPacketFromSupplicant(responseMd5RadiusPacket);
281 assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 3));
282 assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
283
284 // State machine should be in pending state
285
286 assertThat(stateMachine, notNullValue());
287 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
288
289 // (5) RADIUS Success
290
291 RADIUS successPacket =
292 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
293 aaaManager.handleRadiusPacket((successPacket));
294 Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
295
296 checkRadiusPacket(aaaManager, supplicantSuccessPacket, EAP.SUCCESS);
297
298 // State machine should be in authorized state
299
300 assertThat(stateMachine, notNullValue());
301 assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
302
303 // Counts the aaa Statistics count and displays in the log
304 countAaaStatistics();
305
306 }
307
308 /** Tests the count for defected packets.
309 *
310 * @throws DeserializationException
311 * if packed deserialization fails.
312 */
313 @Test
314 public void testAaaStatisticsForDefectivePackets() throws Exception {
315 // (1) Supplicant start up
316 Ethernet startPacket = constructSupplicantStartPacket();
317 sendPacket(startPacket);
318
319 // (2) Supplicant identify
320
321 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
322 sendPacket(identifyPacket);
323
324 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
325
326 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
327
328 // Calling the mock test socket based to handle packet
329 aaaManager.impl.handlePacketFromServer(null);
330 // State machine should have been created by now
331
332 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(SESSION_ID);
333
334 // (3) RADIUS MD5 challenge
335
336 RADIUS radiusCodeAccessChallengePacket = constructRadiusCodeAccessChallengePacket(
337 RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
338 aaaManager.handleRadiusPacket(radiusCodeAccessChallengePacket);
339
340 Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
341
342 // (4) Supplicant MD5 response
343
344 Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(stateMachine, EAP.ATTR_MD5,
345 stateMachine.challengeIdentifier(), radiusChallengeMD5Packet);
346 sendPacket(md5RadiusPacket);
347 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
348 // (5) RADIUS Rejected
349
350 RADIUS rejectedPacket =
351 constructRadiusCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_REJECT, EAP.FAILURE);
352 aaaManager.handleRadiusPacket((rejectedPacket));
353 Ethernet supplicantRejectedPacket = (Ethernet) fetchPacket(4);
354
355 checkRadiusPacket(aaaManager, supplicantRejectedPacket, EAP.FAILURE);
356
357 // State machine should be in unauthorized state
358 assertThat(stateMachine, notNullValue());
359 assertThat(stateMachine.state(), is(StateMachine.STATE_UNAUTHORIZED));
360 // Calculated the total round trip time
361 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
362 // Counts the aaa Statistics count and displays in the log
363 countAaaStatistics();
364
365 }
366
367 /*
368 * Tests the retransmitted packet and malformed packet count
369 *
370 * @throws DeserializationException
371 * if packed deserialization fails.
372 */
373 @Test
374 public void testRequestRetransmittedCount() throws Exception {
375
376 // (1) Supplicant start up
377 Ethernet startPacket = constructSupplicantStartPacket();
378 sendPacket(startPacket);
379
380 // (2) Supplicant identify
381
382 Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
383 sendPacket(identifyPacket);
384
385 RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
386 checkRadiusPacketFromSupplicant(radiusIdentifyPacket);
387
388 // again creating pending state for same packet
389 constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
390 sendPacket(identifyPacket);
391 aaaManager.impl.handlePacketFromServer(null);
392 aaaManager.aaaStatisticsManager.calculatePacketRoundtripTime();
393
394 // creating malformed packet
395 final ByteBuffer byteBuffer = ByteBuffer.wrap(startPacket.serialize());
396 InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
397 startPacket, byteBuffer);
398
399 PacketContext context = new TestPacketContext(127L, inPacket, null, false);
400 aaaManager.impl.handlePacketFromServer(context);
401 countAaaStatistics();
402
403 }
404
405 // Calculates the AAA statistics count.
406 public void countAaaStatistics() {
407 assertThat(aaaStatisticsManager.getAaaStats().getAcceptResponsesRx(), notNullValue());
408 assertThat(aaaStatisticsManager.getAaaStats().getAccessRequestsTx(), notNullValue());
409 assertThat(aaaStatisticsManager.getAaaStats().getChallengeResponsesRx(), notNullValue());
410 assertThat(aaaStatisticsManager.getAaaStats().getDroppedResponsesRx(), notNullValue());
411 assertThat(aaaStatisticsManager.getAaaStats().getInvalidValidatorsRx(), notNullValue());
412 assertThat(aaaStatisticsManager.getAaaStats().getMalformedResponsesRx(), notNullValue());
413 assertThat(aaaStatisticsManager.getAaaStats().getPendingRequests(), notNullValue());
414 assertThat(aaaStatisticsManager.getAaaStats().getRejectResponsesRx(), notNullValue());
415 assertThat(aaaStatisticsManager.getAaaStats().getRequestReTx(), notNullValue());
416 assertThat(aaaStatisticsManager.getAaaStats().getRequestRttMilis(), notNullValue());
417 assertThat(aaaStatisticsManager.getAaaStats().getUnknownServerRx(), notNullValue());
418 assertThat(aaaStatisticsManager.getAaaStats().getUnknownTypeRx(), notNullValue());
419 }
420
421 /*
422 * Mock implementation of SocketBasedRadiusCommunicator class.
423 *
424 */
425 class TestSocketBasedRadiusCommunicator extends SocketBasedRadiusCommunicator {
426
427 TestSocketBasedRadiusCommunicator(ApplicationId appId, PacketService pktService, AaaManager aaaManager) {
428 super(appId, pktService, aaaManager);
429 }
430
431 // Implementation of socketBasedRadiusCommunicator--> run() method
432 public void handlePacketFromServer(PacketContext context) {
433
434 RADIUS incomingPkt = (RADIUS) fetchPacket(savedPackets.size() - 1);
435 try {
436 if (context == null) {
437 aaaStatisticsManager.handleRoundtripTime(incomingPkt.getIdentifier());
438 aaaManager.handleRadiusPacket(incomingPkt);
439 } else if (null != context) {
440 aaaManager.checkForPacketFromUnknownServer("100.100.100.0");
441 aaaStatisticsManager.handleRoundtripTime(incomingPkt.getIdentifier());
442 aaaManager.handleRadiusPacket(incomingPkt);
443 incomingPkt =
444 RADIUS.deserializer().deserialize(incomingPkt.generateAuthCode(), 0, 1);
445 }
446 } catch (DeserializationException dex) {
447 aaaManager.aaaStatisticsManager.getAaaStats().increaseMalformedResponsesRx();
448 aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
449 log.error("Cannot deserialize packet", dex);
450 } catch (StateMachineException sme) {
451 log.error("Illegal state machine operation", sme);
452 }
453
454 }
455
456 }
457
458}