Ray Milkey
Committed by Gerrit Code Review

Support RADIUS server outside of the ONOS network

Change-Id: I7e64faae6831467e084db878e02023d40fb33f07
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
15 */ 15 */
16 package org.onosproject.aaa; 16 package org.onosproject.aaa;
17 17
18 +import java.io.IOException;
19 +import java.net.DatagramPacket;
20 +import java.net.DatagramSocket;
18 import java.net.InetAddress; 21 import java.net.InetAddress;
19 import java.nio.ByteBuffer; 22 import java.nio.ByteBuffer;
20 -import java.util.Optional; 23 +import java.util.concurrent.ExecutorService;
21 -import java.util.Set; 24 +import java.util.concurrent.Executors;
22 25
23 import org.apache.felix.scr.annotations.Activate; 26 import org.apache.felix.scr.annotations.Activate;
24 import org.apache.felix.scr.annotations.Component; 27 import org.apache.felix.scr.annotations.Component;
...@@ -31,19 +34,14 @@ import org.onlab.packet.EAPOL; ...@@ -31,19 +34,14 @@ import org.onlab.packet.EAPOL;
31 import org.onlab.packet.EthType; 34 import org.onlab.packet.EthType;
32 import org.onlab.packet.Ethernet; 35 import org.onlab.packet.Ethernet;
33 import org.onlab.packet.IPv4; 36 import org.onlab.packet.IPv4;
34 -import org.onlab.packet.Ip4Address;
35 -import org.onlab.packet.IpAddress;
36 import org.onlab.packet.MacAddress; 37 import org.onlab.packet.MacAddress;
37 import org.onlab.packet.RADIUS; 38 import org.onlab.packet.RADIUS;
38 import org.onlab.packet.RADIUSAttribute; 39 import org.onlab.packet.RADIUSAttribute;
39 import org.onlab.packet.TpPort; 40 import org.onlab.packet.TpPort;
40 -import org.onlab.packet.UDP;
41 -import org.onlab.packet.VlanId;
42 import org.onosproject.core.ApplicationId; 41 import org.onosproject.core.ApplicationId;
43 import org.onosproject.core.CoreService; 42 import org.onosproject.core.CoreService;
44 import org.onosproject.net.ConnectPoint; 43 import org.onosproject.net.ConnectPoint;
45 import org.onosproject.net.DeviceId; 44 import org.onosproject.net.DeviceId;
46 -import org.onosproject.net.Host;
47 import org.onosproject.net.PortNumber; 45 import org.onosproject.net.PortNumber;
48 import org.onosproject.net.config.ConfigFactory; 46 import org.onosproject.net.config.ConfigFactory;
49 import org.onosproject.net.config.NetworkConfigEvent; 47 import org.onosproject.net.config.NetworkConfigEvent;
...@@ -53,7 +51,6 @@ import org.onosproject.net.flow.DefaultTrafficSelector; ...@@ -53,7 +51,6 @@ import org.onosproject.net.flow.DefaultTrafficSelector;
53 import org.onosproject.net.flow.DefaultTrafficTreatment; 51 import org.onosproject.net.flow.DefaultTrafficTreatment;
54 import org.onosproject.net.flow.TrafficSelector; 52 import org.onosproject.net.flow.TrafficSelector;
55 import org.onosproject.net.flow.TrafficTreatment; 53 import org.onosproject.net.flow.TrafficTreatment;
56 -import org.onosproject.net.host.HostService;
57 import org.onosproject.net.packet.DefaultOutboundPacket; 54 import org.onosproject.net.packet.DefaultOutboundPacket;
58 import org.onosproject.net.packet.InboundPacket; 55 import org.onosproject.net.packet.InboundPacket;
59 import org.onosproject.net.packet.OutboundPacket; 56 import org.onosproject.net.packet.OutboundPacket;
...@@ -63,6 +60,8 @@ import org.onosproject.net.packet.PacketService; ...@@ -63,6 +60,8 @@ import org.onosproject.net.packet.PacketService;
63 import org.onosproject.xosintegration.VoltTenantService; 60 import org.onosproject.xosintegration.VoltTenantService;
64 import org.slf4j.Logger; 61 import org.slf4j.Logger;
65 62
63 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
64 +
66 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; 65 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
67 import static org.onosproject.net.packet.PacketPriority.CONTROL; 66 import static org.onosproject.net.packet.PacketPriority.CONTROL;
68 import static org.slf4j.LoggerFactory.getLogger; 67 import static org.slf4j.LoggerFactory.getLogger;
...@@ -85,10 +84,6 @@ public class AAA { ...@@ -85,10 +84,6 @@ public class AAA {
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected PacketService packetService; 85 protected PacketService packetService;
87 86
88 - // end host information
89 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 - protected HostService hostService;
91 -
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected VoltTenantService voltTenantService; 88 protected VoltTenantService voltTenantService;
94 89
...@@ -121,6 +116,12 @@ public class AAA { ...@@ -121,6 +116,12 @@ public class AAA {
121 // our unique identifier 116 // our unique identifier
122 private ApplicationId appId; 117 private ApplicationId appId;
123 118
119 + // Socket used for UDP communications with RADIUS server
120 + private DatagramSocket radiusSocket;
121 +
122 + // Executor for RADIUS communication thread
123 + private ExecutorService executor;
124 +
124 // Configuration properties factory 125 // Configuration properties factory
125 private final ConfigFactory factory = 126 private final ConfigFactory factory =
126 new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY, 127 new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY,
...@@ -184,7 +185,16 @@ public class AAA { ...@@ -184,7 +185,16 @@ public class AAA {
184 185
185 StateMachine.initializeMaps(); 186 StateMachine.initializeMaps();
186 187
187 - hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); 188 + try {
189 + radiusSocket = new DatagramSocket(radiusServerPort);
190 + } catch (Exception ex) {
191 + log.error("Can't open RADIUS socket", ex);
192 + }
193 +
194 + executor = Executors.newSingleThreadExecutor(
195 + new ThreadFactoryBuilder()
196 + .setNameFormat("AAA-radius-%d").build());
197 + executor.execute(radiusListener);
188 } 198 }
189 199
190 @Deactivate 200 @Deactivate
...@@ -195,6 +205,24 @@ public class AAA { ...@@ -195,6 +205,24 @@ public class AAA {
195 packetService.removeProcessor(processor); 205 packetService.removeProcessor(processor);
196 processor = null; 206 processor = null;
197 StateMachine.destroyMaps(); 207 StateMachine.destroyMaps();
208 + radiusSocket.close();
209 + executor.shutdownNow();
210 + }
211 +
212 + protected void sendRADIUSPacket(RADIUS radiusPacket) {
213 +
214 + try {
215 + final byte[] data = radiusPacket.serialize();
216 + final DatagramSocket socket = radiusSocket;
217 +
218 + DatagramPacket packet =
219 + new DatagramPacket(data, data.length,
220 + radiusIpAddress, radiusServerPort);
221 +
222 + socket.send(packet);
223 + } catch (IOException e) {
224 + log.info("Cannot send packet to RADIUS server", e);
225 + }
198 } 226 }
199 227
200 /** 228 /**
...@@ -232,6 +260,19 @@ public class AAA { ...@@ -232,6 +260,19 @@ public class AAA {
232 packetService.cancelPackets(radSelector, CONTROL, appId); 260 packetService.cancelPackets(radSelector, CONTROL, appId);
233 } 261 }
234 262
263 + /**
264 + * Send the ethernet packet to the supplicant.
265 + *
266 + * @param ethernetPkt the ethernet packet
267 + * @param connectPoint the connect point to send out
268 + */
269 + private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
270 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
271 + OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
272 + treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
273 + packetService.emit(packet);
274 + }
275 +
235 // our handler defined as a private inner class 276 // our handler defined as a private inner class
236 277
237 /** 278 /**
...@@ -253,26 +294,11 @@ public class AAA { ...@@ -253,26 +294,11 @@ public class AAA {
253 case EAPOL: 294 case EAPOL:
254 handleSupplicantPacket(context.inPacket()); 295 handleSupplicantPacket(context.inPacket());
255 break; 296 break;
256 - case IPV4:
257 - IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
258 - Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
259 - Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress);
260 - if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
261 - // TODO: check for port as well when it's configurable
262 - UDP udpPacket = (UDP) ipv4Packet.getPayload();
263 -
264 - byte[] datagram = udpPacket.getPayload().serialize();
265 - RADIUS radiusPacket;
266 - radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
267 - handleRadiusPacket(radiusPacket);
268 - }
269 -
270 - break;
271 default: 297 default:
272 log.trace("Skipping Ethernet packet type {}", 298 log.trace("Skipping Ethernet packet type {}",
273 EthType.EtherType.lookup(ethPkt.getEtherType())); 299 EthType.EtherType.lookup(ethPkt.getEtherType()));
274 } 300 }
275 - } catch (DeserializationException | StateMachineException e) { 301 + } catch (StateMachineException e) {
276 log.warn("Unable to process RADIUS packet:", e); 302 log.warn("Unable to process RADIUS packet:", e);
277 } 303 }
278 } 304 }
...@@ -280,23 +306,26 @@ public class AAA { ...@@ -280,23 +306,26 @@ public class AAA {
280 /** 306 /**
281 * Creates and initializes common fields of a RADIUS packet. 307 * Creates and initializes common fields of a RADIUS packet.
282 * 308 *
283 - * @param identifier RADIUS identifier 309 + * @param stateMachine state machine for the request
284 * @param eapPacket EAP packet 310 * @param eapPacket EAP packet
285 * @return RADIUS packet 311 * @return RADIUS packet
286 */ 312 */
287 - private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) { 313 + private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
288 RADIUS radiusPayload = 314 RADIUS radiusPayload =
289 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, 315 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
290 eapPacket.getIdentifier()); 316 eapPacket.getIdentifier());
317 +
318 + // set Request Authenticator in StateMachine
319 + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
320 +
291 radiusPayload.setIdentifier(identifier); 321 radiusPayload.setIdentifier(identifier);
292 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, 322 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
293 - eapPacket.getData()); 323 + stateMachine.username());
294 324
295 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, 325 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
296 AAA.this.nasIpAddress.getAddress()); 326 AAA.this.nasIpAddress.getAddress());
297 327
298 radiusPayload.encapsulateMessage(eapPacket); 328 radiusPayload.encapsulateMessage(eapPacket);
299 - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
300 329
301 return radiusPayload; 330 return radiusPayload;
302 } 331 }
...@@ -329,13 +358,13 @@ public class AAA { ...@@ -329,13 +358,13 @@ public class AAA {
329 358
330 //send an EAP Request/Identify to the supplicant 359 //send an EAP Request/Identify to the supplicant
331 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null); 360 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
332 - Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L), 361 + Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(nasMacAddress),
333 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, 362 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
334 eapPayload); 363 eapPayload);
335 stateMachine.setSupplicantAddress(srcMAC); 364 stateMachine.setSupplicantAddress(srcMAC);
336 stateMachine.setVlanId(ethPkt.getVlanID()); 365 stateMachine.setVlanId(ethPkt.getVlanID());
337 366
338 - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); 367 + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
339 368
340 break; 369 break;
341 case EAPOL.EAPOL_PACKET: 370 case EAPOL.EAPOL_PACKET:
...@@ -350,11 +379,10 @@ public class AAA { ...@@ -350,11 +379,10 @@ public class AAA {
350 // request id access to RADIUS 379 // request id access to RADIUS
351 stateMachine.setUsername(eapPacket.getData()); 380 stateMachine.setUsername(eapPacket.getData());
352 381
353 - radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); 382 + radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
383 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
354 384
355 - // set Request Authenticator in StateMachine 385 + sendRADIUSPacket(radiusPayload);
356 - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
357 - sendRadiusMessage(radiusPayload);
358 386
359 // change the state to "PENDING" 387 // change the state to "PENDING"
360 stateMachine.requestAccess(); 388 stateMachine.requestAccess();
...@@ -365,22 +393,27 @@ public class AAA { ...@@ -365,22 +393,27 @@ public class AAA {
365 // machine. 393 // machine.
366 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) { 394 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
367 //send the RADIUS challenge response 395 //send the RADIUS challenge response
368 - radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket); 396 + radiusPayload =
397 + getRadiusPayload(stateMachine,
398 + stateMachine.identifier(),
399 + eapPacket);
369 400
370 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, 401 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
371 stateMachine.challengeState()); 402 stateMachine.challengeState());
372 - sendRadiusMessage(radiusPayload); 403 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
404 + sendRADIUSPacket(radiusPayload);
373 } 405 }
374 break; 406 break;
375 case EAP.ATTR_TLS: 407 case EAP.ATTR_TLS:
376 // request id access to RADIUS 408 // request id access to RADIUS
377 - radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); 409 + radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
378 410
379 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, 411 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
380 stateMachine.challengeState()); 412 stateMachine.challengeState());
381 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); 413 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
382 414
383 - sendRadiusMessage(radiusPayload); 415 + sendRADIUSPacket(radiusPayload);
416 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
384 // TODO: this gets called on every fragment, should only be called at TLS-Start 417 // TODO: this gets called on every fragment, should only be called at TLS-Start
385 stateMachine.requestAccess(); 418 stateMachine.requestAccess();
386 419
...@@ -392,14 +425,18 @@ public class AAA { ...@@ -392,14 +425,18 @@ public class AAA {
392 default: 425 default:
393 log.trace("Skipping EAPOL message {}", eapol.getEapolType()); 426 log.trace("Skipping EAPOL message {}", eapol.getEapolType());
394 } 427 }
428 +
429 + }
395 } 430 }
396 431
432 + class RadiusListener implements Runnable {
433 +
397 /** 434 /**
398 * Handles RADIUS packets. 435 * Handles RADIUS packets.
399 * 436 *
400 * @param radiusPacket RADIUS packet coming from the RADIUS server. 437 * @param radiusPacket RADIUS packet coming from the RADIUS server.
401 */ 438 */
402 - private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException { 439 + protected void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
403 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier()); 440 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
404 if (stateMachine == null) { 441 if (stateMachine == null) {
405 log.error("Invalid session identifier, exiting..."); 442 log.error("Invalid session identifier, exiting...");
...@@ -410,13 +447,16 @@ public class AAA { ...@@ -410,13 +447,16 @@ public class AAA {
410 Ethernet eth; 447 Ethernet eth;
411 switch (radiusPacket.getCode()) { 448 switch (radiusPacket.getCode()) {
412 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE: 449 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
413 - byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue(); 450 + byte[] challengeState =
451 + radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
414 eapPayload = radiusPacket.decapsulateMessage(); 452 eapPayload = radiusPacket.decapsulateMessage();
415 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState); 453 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
416 eth = buildEapolResponse(stateMachine.supplicantAddress(), 454 eth = buildEapolResponse(stateMachine.supplicantAddress(),
417 - MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, 455 + MacAddress.valueOf(nasMacAddress),
456 + stateMachine.vlanId(),
457 + EAPOL.EAPOL_PACKET,
418 eapPayload); 458 eapPayload);
419 - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); 459 + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
420 break; 460 break;
421 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT: 461 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
422 //send an EAPOL - Success to the supplicant. 462 //send an EAPOL - Success to the supplicant.
...@@ -425,9 +465,11 @@ public class AAA { ...@@ -425,9 +465,11 @@ public class AAA {
425 eapPayload = new EAP(); 465 eapPayload = new EAP();
426 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); 466 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
427 eth = buildEapolResponse(stateMachine.supplicantAddress(), 467 eth = buildEapolResponse(stateMachine.supplicantAddress(),
428 - MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, 468 + MacAddress.valueOf(nasMacAddress),
469 + stateMachine.vlanId(),
470 + EAPOL.EAPOL_PACKET,
429 eapPayload); 471 eapPayload);
430 - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); 472 + sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
431 473
432 stateMachine.authorizeAccess(); 474 stateMachine.authorizeAccess();
433 break; 475 break;
...@@ -439,58 +481,42 @@ public class AAA { ...@@ -439,58 +481,42 @@ public class AAA {
439 } 481 }
440 } 482 }
441 483
442 - private void sendRadiusMessage(RADIUS radiusMessage) {
443 - Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
444 - Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
445 484
446 - Host dst; 485 + @Override
447 - if (!odst.isPresent()) { 486 + public void run() {
448 - log.info("Radius server {} is not present", radiusIpAddress); 487 + boolean done = false;
449 - return; 488 + int packetNumber = 1;
450 - } else {
451 - dst = odst.get();
452 - }
453 -
454 - UDP udp = new UDP();
455 - IPv4 ip4Packet = new IPv4();
456 - Ethernet ethPkt = new Ethernet();
457 - radiusMessage.setParent(udp);
458 - udp.setDestinationPort(radiusServerPort);
459 - udp.setSourcePort(radiusServerPort);
460 - udp.setPayload(radiusMessage);
461 - udp.setParent(ip4Packet);
462 - ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress());
463 - ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress());
464 - ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
465 - ip4Packet.setPayload(udp);
466 - ip4Packet.setParent(ethPkt);
467 - ethPkt.setDestinationMACAddress(radiusMacAddress);
468 - ethPkt.setSourceMACAddress(nasMacAddress);
469 - ethPkt.setEtherType(Ethernet.TYPE_IPV4);
470 - ethPkt.setPayload(ip4Packet);
471 -
472 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
473 - .setOutput(PortNumber.portNumber(radiusPort)).build();
474 - OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
475 - treatment, ByteBuffer.wrap(ethPkt.serialize()));
476 - packetService.emit(packet);
477 489
490 + log.info("UDP listener thread starting up");
491 + RADIUS inboundRadiusPacket;
492 + while (!done) {
493 + try {
494 + DatagramPacket inboundBasePacket = new DatagramPacket(new byte[1000], 1000);
495 + DatagramSocket socket = radiusSocket;
496 + socket.receive(inboundBasePacket);
497 + log.info("Packet #{} received", packetNumber++);
498 + try {
499 + inboundRadiusPacket =
500 + RADIUS.deserializer()
501 + .deserialize(inboundBasePacket.getData(),
502 + 0,
503 + inboundBasePacket.getLength());
504 + handleRadiusPacket(inboundRadiusPacket);
505 + } catch (DeserializationException dex) {
506 + log.error("Cannot deserialize packet", dex);
507 + } catch (StateMachineException sme) {
508 + log.error("Illegal state machine operation", sme);
478 } 509 }
479 510
480 - /** 511 + } catch (IOException e) {
481 - * Send the ethernet packet to the supplicant. 512 + log.info("Socket was closed, exiting listener thread");
482 - * 513 + done = true;
483 - * @param ethernetPkt the ethernet packet 514 + }
484 - * @param connectPoint the connect point to send out
485 - */
486 - private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
487 - TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
488 - OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
489 - treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
490 - packetService.emit(packet);
491 } 515 }
492 -
493 } 516 }
517 + }
518 +
519 + RadiusListener radiusListener = new RadiusListener();
494 520
495 private class InternalConfigListener implements NetworkConfigListener { 521 private class InternalConfigListener implements NetworkConfigListener {
496 522
......
...@@ -37,13 +37,13 @@ public class AAAConfig extends Config<ApplicationId> { ...@@ -37,13 +37,13 @@ public class AAAConfig extends Config<ApplicationId> {
37 private static final String RADIUS_PORT = "radiusPort"; 37 private static final String RADIUS_PORT = "radiusPort";
38 38
39 // RADIUS server IP address 39 // RADIUS server IP address
40 - protected static final String DEFAULT_RADIUS_IP = "192.168.1.10"; 40 + protected static final String DEFAULT_RADIUS_IP = "10.128.10.4";
41 41
42 // RADIUS MAC address 42 // RADIUS MAC address
43 protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10"; 43 protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10";
44 44
45 // NAS IP address 45 // NAS IP address
46 - protected static final String DEFAULT_NAS_IP = "192.168.1.11"; 46 + protected static final String DEFAULT_NAS_IP = "10.128.9.244";
47 47
48 // NAS MAC address 48 // NAS MAC address
49 protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01"; 49 protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01";
......
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
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 + */
16 +package org.onosproject.aaa;
17 +
18 +import org.junit.Before;
19 +import org.junit.Ignore;
20 +import org.junit.Test;
21 +import org.onlab.packet.EAP;
22 +import org.onlab.packet.EAPOL;
23 +import org.onlab.packet.Ethernet;
24 +import org.onosproject.core.CoreServiceAdapter;
25 +import org.onosproject.net.config.Config;
26 +import org.onosproject.net.config.NetworkConfigRegistryAdapter;
27 +
28 +import static org.hamcrest.Matchers.is;
29 +import static org.hamcrest.Matchers.notNullValue;
30 +import static org.junit.Assert.assertThat;
31 +
32 +/**
33 + * Set of tests of the ONOS application component. These use an existing RADIUS
34 + * server and sends live packets over the network to it.
35 + */
36 +@Ignore ("This should not be run as part of the standard build")
37 +public class AAAIntegrationTest extends AAATestBase {
38 +
39 + private AAA aaa;
40 +
41 + /**
42 + * Mocks the network config registry.
43 + */
44 + @SuppressWarnings("unchecked")
45 + static final class TestNetworkConfigRegistry
46 + extends NetworkConfigRegistryAdapter {
47 + @Override
48 + public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
49 + return (C) new AAAConfig();
50 + }
51 + }
52 +
53 + /**
54 + * Sets up the services required by the AAA application.
55 + */
56 + @Before
57 + public void setUp() {
58 + aaa = new AAA();
59 + aaa.netCfgService = new TestNetworkConfigRegistry();
60 + aaa.coreService = new CoreServiceAdapter();
61 + aaa.packetService = new MockPacketService();
62 + aaa.activate();
63 + }
64 +
65 + /**
66 + * Fetches the sent packet at the given index. The requested packet
67 + * must be the last packet on the list.
68 + *
69 + * @param index index into sent packets array
70 + * @return packet
71 + */
72 + private Ethernet fetchPacket(int index) {
73 + for (int iteration = 0; iteration < 20; iteration++) {
74 + if (savedPackets.size() > index) {
75 + return (Ethernet) savedPackets.get(index);
76 + } else {
77 + try {
78 + Thread.sleep(250);
79 + } catch (Exception ex) {
80 + return null;
81 + }
82 + }
83 + }
84 + return null;
85 + }
86 +
87 + /**
88 + * Tests the authentication path through the AAA application by sending
89 + * packets to the RADIUS server and checking the state machine
90 + * transitions.
91 + *
92 + * @throws Exception when an unhandled error occurs
93 + */
94 + @Test
95 + public void testAuthentication() throws Exception {
96 +
97 + // (1) Supplicant start up
98 +
99 + Ethernet startPacket = constructSupplicantStartPacket();
100 + sendPacket(startPacket);
101 +
102 + Ethernet responsePacket = fetchPacket(0);
103 + assertThat(responsePacket, notNullValue());
104 + checkRadiusPacket(aaa, responsePacket, EAP.REQUEST);
105 +
106 + // (2) Supplicant identify
107 +
108 + Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
109 + sendPacket(identifyPacket);
110 +
111 + // State machine should have been created by now
112 +
113 + StateMachine stateMachine =
114 + StateMachine.lookupStateMachineBySessionId(SESSION_ID);
115 + assertThat(stateMachine, notNullValue());
116 + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
117 +
118 + // (3) RADIUS MD5 challenge
119 +
120 + Ethernet radiusChallengeMD5Packet = fetchPacket(1);
121 + assertThat(radiusChallengeMD5Packet, notNullValue());
122 + checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.REQUEST);
123 +
124 +
125 + // (4) Supplicant MD5 response
126 +
127 + Ethernet md5RadiusPacket =
128 + constructSupplicantIdentifyPacket(stateMachine,
129 + EAP.ATTR_MD5,
130 + stateMachine.challengeIdentifier(),
131 + radiusChallengeMD5Packet);
132 + sendPacket(md5RadiusPacket);
133 +
134 +
135 + // (5) RADIUS Success
136 +
137 + Ethernet successRadiusPacket = fetchPacket(2);
138 + assertThat(successRadiusPacket, notNullValue());
139 + EAPOL successEAPOL = (EAPOL) successRadiusPacket.getPayload();
140 + EAP successEAP = (EAP) successEAPOL.getPayload();
141 + assertThat(successEAP.getCode(), is(EAP.SUCCESS));
142 +
143 + // State machine should be in authorized state
144 +
145 + assertThat(stateMachine, notNullValue());
146 + assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
147 +
148 + }
149 +
150 +}
151 +
...@@ -15,160 +15,57 @@ ...@@ -15,160 +15,57 @@
15 */ 15 */
16 package org.onosproject.aaa; 16 package org.onosproject.aaa;
17 17
18 -import java.nio.ByteBuffer; 18 +import java.net.InetAddress;
19 -import java.util.LinkedList; 19 +import java.net.UnknownHostException;
20 -import java.util.List;
21 -import java.util.Set;
22 20
23 import org.junit.After; 21 import org.junit.After;
24 import org.junit.Before; 22 import org.junit.Before;
25 import org.junit.Test; 23 import org.junit.Test;
26 -import org.onlab.packet.Data; 24 +import org.onlab.packet.BasePacket;
27 import org.onlab.packet.DeserializationException; 25 import org.onlab.packet.DeserializationException;
28 import org.onlab.packet.EAP; 26 import org.onlab.packet.EAP;
29 -import org.onlab.packet.EAPOL;
30 -import org.onlab.packet.EthType;
31 import org.onlab.packet.Ethernet; 27 import org.onlab.packet.Ethernet;
32 -import org.onlab.packet.IPv4;
33 import org.onlab.packet.IpAddress; 28 import org.onlab.packet.IpAddress;
34 -import org.onlab.packet.MacAddress;
35 import org.onlab.packet.RADIUS; 29 import org.onlab.packet.RADIUS;
36 import org.onlab.packet.RADIUSAttribute; 30 import org.onlab.packet.RADIUSAttribute;
37 -import org.onlab.packet.UDP;
38 -import org.onlab.packet.VlanId;
39 import org.onosproject.core.CoreServiceAdapter; 31 import org.onosproject.core.CoreServiceAdapter;
40 -import org.onosproject.net.Annotations;
41 -import org.onosproject.net.Host;
42 -import org.onosproject.net.HostId;
43 -import org.onosproject.net.HostLocation;
44 import org.onosproject.net.config.Config; 32 import org.onosproject.net.config.Config;
45 import org.onosproject.net.config.NetworkConfigRegistryAdapter; 33 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
46 -import org.onosproject.net.host.HostServiceAdapter;
47 -import org.onosproject.net.packet.DefaultInboundPacket;
48 -import org.onosproject.net.packet.DefaultPacketContext;
49 -import org.onosproject.net.packet.InboundPacket;
50 -import org.onosproject.net.packet.OutboundPacket;
51 -import org.onosproject.net.packet.PacketContext;
52 -import org.onosproject.net.packet.PacketProcessor;
53 -import org.onosproject.net.packet.PacketServiceAdapter;
54 -import org.onosproject.net.provider.ProviderId;
55 34
56 import com.google.common.base.Charsets; 35 import com.google.common.base.Charsets;
57 -import com.google.common.collect.ImmutableSet;
58 36
59 -import static org.hamcrest.Matchers.instanceOf;
60 import static org.hamcrest.Matchers.is; 37 import static org.hamcrest.Matchers.is;
61 import static org.hamcrest.Matchers.notNullValue; 38 import static org.hamcrest.Matchers.notNullValue;
62 import static org.junit.Assert.assertThat; 39 import static org.junit.Assert.assertThat;
63 -import static org.junit.Assert.fail;
64 -import static org.onosproject.net.NetTestTools.connectPoint;
65 40
66 /** 41 /**
67 * Set of tests of the ONOS application component. 42 * Set of tests of the ONOS application component.
68 */ 43 */
69 -public class AAATest { 44 +public class AAATest extends AAATestBase {
70 45
71 - MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); 46 + static final String BAD_IP_ADDRESS = "198.51.100.0";
72 - MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
73 47
74 - PacketProcessor packetProcessor;
75 private AAA aaa; 48 private AAA aaa;
76 - List<Ethernet> savedPackets = new LinkedList<>();
77 49
78 - /** 50 + class AAAWithoutRadiusServer extends AAA {
79 - * Saves the given packet onto the saved packets list. 51 + protected void sendRADIUSPacket(RADIUS radiusPacket) {
80 - * 52 + savePacket(radiusPacket);
81 - * @param eth packet to save
82 - */
83 - private void savePacket(Ethernet eth) {
84 - savedPackets.add(eth);
85 - }
86 -
87 - /**
88 - * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
89 - */
90 - private class MockPacketService extends PacketServiceAdapter {
91 -
92 - @Override
93 - public void addProcessor(PacketProcessor processor, int priority) {
94 - packetProcessor = processor;
95 - }
96 -
97 - @Override
98 - public void emit(OutboundPacket packet) {
99 - try {
100 - Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
101 - 0, packet.data().array().length);
102 - savePacket(eth);
103 - } catch (Exception e) {
104 - fail(e.getMessage());
105 - }
106 - }
107 - }
108 -
109 - /**
110 - * Mocks the DefaultPacketContext.
111 - */
112 - private final class TestPacketContext extends DefaultPacketContext {
113 -
114 - private TestPacketContext(long time, InboundPacket inPkt,
115 - OutboundPacket outPkt, boolean block) {
116 - super(time, inPkt, outPkt, block);
117 - }
118 -
119 - @Override
120 - public void send() {
121 - // We don't send anything out.
122 } 53 }
123 } 54 }
124 55
125 /** 56 /**
126 - * Mocks a host to allow locating the Radius server. 57 + * Mocks the AAAConfig class to force usage of an unroutable address for the
58 + * RADIUS server.
127 */ 59 */
128 - private static final class MockHost implements Host { 60 + static class MockAAAConfig extends AAAConfig {
129 @Override 61 @Override
130 - public HostId id() { 62 + public InetAddress radiusIp() {
131 - return null; 63 + try {
132 - } 64 + return InetAddress.getByName(BAD_IP_ADDRESS);
133 - 65 + } catch (UnknownHostException ex) {
134 - @Override 66 + // can't happen
135 - public MacAddress mac() { 67 + throw new IllegalStateException(ex);
136 - return null;
137 - }
138 -
139 - @Override
140 - public VlanId vlan() {
141 - return VlanId.vlanId(VlanId.UNTAGGED);
142 - }
143 -
144 - @Override
145 - public Set<IpAddress> ipAddresses() {
146 - return null;
147 - }
148 -
149 - @Override
150 - public HostLocation location() {
151 - return null;
152 } 68 }
153 -
154 - @Override
155 - public Annotations annotations() {
156 - return null;
157 - }
158 -
159 - @Override
160 - public ProviderId providerId() {
161 - return null;
162 - }
163 - }
164 -
165 - /**
166 - * Mocks the Host service.
167 - */
168 - private static final class MockHostService extends HostServiceAdapter {
169 - @Override
170 - public Set<Host> getHostsByIp(IpAddress ip) {
171 - return ImmutableSet.of(new MockHost());
172 } 69 }
173 } 70 }
174 71
...@@ -180,80 +77,9 @@ public class AAATest { ...@@ -180,80 +77,9 @@ public class AAATest {
180 extends NetworkConfigRegistryAdapter { 77 extends NetworkConfigRegistryAdapter {
181 @Override 78 @Override
182 public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) { 79 public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
183 - return (C) new AAAConfig(); 80 + AAAConfig aaaConfig = new MockAAAConfig();
184 - } 81 + return (C) aaaConfig;
185 - }
186 -
187 - /**
188 - * Sends an Ethernet packet to the process method of the Packet Processor.
189 - *
190 - * @param reply Ethernet packet
191 - */
192 - private void sendPacket(Ethernet reply) {
193 - final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
194 - InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
195 - reply,
196 - byteBuffer);
197 -
198 - PacketContext context = new TestPacketContext(127L, inPacket, null, false);
199 - packetProcessor.process(context);
200 - }
201 -
202 - /**
203 - * Constructs an Ethernet packet containing a EAPOL_START Payload.
204 - *
205 - * @return Ethernet packet
206 - */
207 - private Ethernet constructSupplicantStartPacket() {
208 - Ethernet eth = new Ethernet();
209 - eth.setDestinationMACAddress(clientMac.toBytes());
210 - eth.setSourceMACAddress(serverMac.toBytes());
211 - eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
212 - eth.setVlanID((short) 2);
213 -
214 - EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null);
215 -
216 - //eapol header
217 - EAPOL eapol = new EAPOL();
218 - eapol.setEapolType(EAPOL.EAPOL_START);
219 - eapol.setPacketLength(eap.getLength());
220 -
221 - //eap part
222 - eapol.setPayload(eap);
223 -
224 - eth.setPayload(eapol);
225 - eth.setPad(true);
226 - return eth;
227 } 82 }
228 -
229 - /**
230 - * Constructs an Ethernet packet containing identification payload.
231 - *
232 - * @return Ethernet packet
233 - */
234 - private Ethernet constructSupplicantIdentifyPacket(byte type) {
235 - Ethernet eth = new Ethernet();
236 - eth.setDestinationMACAddress(clientMac.toBytes());
237 - eth.setSourceMACAddress(serverMac.toBytes());
238 - eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
239 - eth.setVlanID((short) 2);
240 -
241 - String username = "user";
242 - EAP eap = new EAP(EAP.REQUEST, (byte) 1, type,
243 - username.getBytes(Charsets.US_ASCII));
244 - eap.setIdentifier((byte) 1);
245 -
246 - // eapol header
247 - EAPOL eapol = new EAPOL();
248 - eapol.setEapolType(EAPOL.EAPOL_PACKET);
249 - eapol.setPacketLength(eap.getLength());
250 -
251 - // eap part
252 - eapol.setPayload(eap);
253 -
254 - eth.setPayload(eapol);
255 - eth.setPad(true);
256 - return eth;
257 } 83 }
258 84
259 /** 85 /**
...@@ -264,18 +90,9 @@ public class AAATest { ...@@ -264,18 +90,9 @@ public class AAATest {
264 * @param challengeType type to use in challenge packet 90 * @param challengeType type to use in challenge packet
265 * @return Ethernet packet 91 * @return Ethernet packet
266 */ 92 */
267 - private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) { 93 + private RADIUS constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
268 - Ethernet eth = new Ethernet();
269 - eth.setDestinationMACAddress(clientMac.toBytes());
270 - eth.setSourceMACAddress(serverMac.toBytes());
271 - eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
272 - eth.setVlanID((short) 2);
273 -
274 - IPv4 ipv4 = new IPv4();
275 - ipv4.setProtocol(IPv4.PROTOCOL_UDP);
276 - ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress());
277 94
278 - String challenge = "1234"; 95 + String challenge = "12345678901234567";
279 96
280 EAP eap = new EAP(challengeType, (byte) 1, challengeType, 97 EAP eap = new EAP(challengeType, (byte) 1, challengeType,
281 challenge.getBytes(Charsets.US_ASCII)); 98 challenge.getBytes(Charsets.US_ASCII));
...@@ -291,13 +108,7 @@ public class AAATest { ...@@ -291,13 +108,7 @@ public class AAATest {
291 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE, 108 radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
292 eap.serialize()); 109 eap.serialize());
293 110
294 - UDP udp = new UDP(); 111 + return radius;
295 - udp.setPayload(radius);
296 - ipv4.setPayload(udp);
297 -
298 - eth.setPayload(ipv4);
299 - eth.setPad(true);
300 - return eth;
301 } 112 }
302 113
303 /** 114 /**
...@@ -305,11 +116,10 @@ public class AAATest { ...@@ -305,11 +116,10 @@ public class AAATest {
305 */ 116 */
306 @Before 117 @Before
307 public void setUp() { 118 public void setUp() {
308 - aaa = new AAA(); 119 + aaa = new AAAWithoutRadiusServer();
309 aaa.netCfgService = new TestNetworkConfigRegistry(); 120 aaa.netCfgService = new TestNetworkConfigRegistry();
310 aaa.coreService = new CoreServiceAdapter(); 121 aaa.coreService = new CoreServiceAdapter();
311 aaa.packetService = new MockPacketService(); 122 aaa.packetService = new MockPacketService();
312 - aaa.hostService = new MockHostService();
313 aaa.activate(); 123 aaa.activate();
314 } 124 }
315 125
...@@ -324,60 +134,16 @@ public class AAATest { ...@@ -324,60 +134,16 @@ public class AAATest {
324 /** 134 /**
325 * Extracts the RADIUS packet from a packet sent by the supplicant. 135 * Extracts the RADIUS packet from a packet sent by the supplicant.
326 * 136 *
327 - * @param supplicantPacket packet sent by the supplicant 137 + * @param radius RADIUS packet sent by the supplicant
328 - * @return RADIUS packet
329 * @throws DeserializationException if deserialization of the packet contents 138 * @throws DeserializationException if deserialization of the packet contents
330 * fails. 139 * fails.
331 */ 140 */
332 - private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket) 141 + private void checkRADIUSPacketFromSupplicant(RADIUS radius)
333 throws DeserializationException { 142 throws DeserializationException {
334 - assertThat(supplicantPacket, notNullValue());
335 - assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED));
336 - assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress));
337 - assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress));
338 -
339 - assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class));
340 - IPv4 ipv4 = (IPv4) supplicantPacket.getPayload();
341 - assertThat(ipv4, notNullValue());
342 - assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(),
343 - is(aaa.nasIpAddress.getHostAddress()));
344 - assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(),
345 - is(aaa.radiusIpAddress.getHostAddress()));
346 -
347 - assertThat(ipv4.getPayload(), instanceOf(UDP.class));
348 - UDP udp = (UDP) ipv4.getPayload();
349 - assertThat(udp, notNullValue());
350 -
351 - assertThat(udp.getPayload(), instanceOf(Data.class));
352 - Data data = (Data) udp.getPayload();
353 - RADIUS radius = RADIUS.deserializer()
354 - .deserialize(data.getData(), 0, data.getData().length);
355 assertThat(radius, notNullValue()); 143 assertThat(radius, notNullValue());
356 - return radius;
357 - }
358 -
359 - /**
360 - * Checks the contents of a RADIUS packet being sent to the RADIUS server.
361 - *
362 - * @param radiusPacket packet to check
363 - * @param code expected code
364 - */
365 - private void checkRadiusPacket(Ethernet radiusPacket, byte code) {
366 - assertThat(radiusPacket.getVlanID(), is((short) 2));
367 -
368 - // TODO: These address values seem wrong, but are produced by the current AAA implementation
369 - assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L)));
370 - assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
371 144
372 - assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class)); 145 + EAP eap = radius.decapsulateMessage();
373 - EAPOL eapol = (EAPOL) radiusPacket.getPayload();
374 - assertThat(eapol, notNullValue());
375 -
376 - assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
377 - assertThat(eapol.getPayload(), instanceOf(EAP.class));
378 - EAP eap = (EAP) eapol.getPayload();
379 assertThat(eap, notNullValue()); 146 assertThat(eap, notNullValue());
380 - assertThat(eap.getCode(), is(code));
381 } 147 }
382 148
383 /** 149 /**
...@@ -387,11 +153,10 @@ public class AAATest { ...@@ -387,11 +153,10 @@ public class AAATest {
387 * @param index index into sent packets array 153 * @param index index into sent packets array
388 * @return packet 154 * @return packet
389 */ 155 */
390 - private Ethernet fetchPacket(int index) { 156 + private BasePacket fetchPacket(int index) {
391 - assertThat(savedPackets.size(), is(index + 1)); 157 + BasePacket packet = savedPackets.get(index);
392 - Ethernet eth = savedPackets.get(index); 158 + assertThat(packet, notNullValue());
393 - assertThat(eth, notNullValue()); 159 + return packet;
394 - return eth;
395 } 160 }
396 161
397 /** 162 /**
...@@ -400,61 +165,64 @@ public class AAATest { ...@@ -400,61 +165,64 @@ public class AAATest {
400 * @throws DeserializationException if packed deserialization fails. 165 * @throws DeserializationException if packed deserialization fails.
401 */ 166 */
402 @Test 167 @Test
403 - public void testAuthentication() throws DeserializationException { 168 + public void testAuthentication() throws Exception {
404 -
405 - // Our session id will be the device ID ("of:1") with the port ("1") concatenated
406 - String sessionId = "of:11";
407 169
408 // (1) Supplicant start up 170 // (1) Supplicant start up
409 171
410 Ethernet startPacket = constructSupplicantStartPacket(); 172 Ethernet startPacket = constructSupplicantStartPacket();
411 sendPacket(startPacket); 173 sendPacket(startPacket);
412 174
413 - Ethernet responsePacket = fetchPacket(0); 175 + Ethernet responsePacket = (Ethernet) fetchPacket(0);
414 - checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY); 176 + checkRadiusPacket(aaa, responsePacket, EAP.ATTR_IDENTITY);
415 177
416 // (2) Supplicant identify 178 // (2) Supplicant identify
417 179
418 - Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY); 180 + Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
419 sendPacket(identifyPacket); 181 sendPacket(identifyPacket);
420 182
421 - Ethernet radiusIdentifyPacket = fetchPacket(1); 183 + RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
184 +
185 + checkRADIUSPacketFromSupplicant(radiusIdentifyPacket);
422 186
423 - RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket); 187 + assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
424 - assertThat(radiusAccessRequest, notNullValue()); 188 + assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
425 - assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); 189 + is("testuser"));
426 - assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
427 - is("user"));
428 190
429 IpAddress nasIp = 191 IpAddress nasIp =
430 IpAddress.valueOf(IpAddress.Version.INET, 192 IpAddress.valueOf(IpAddress.Version.INET,
431 - radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP) 193 + radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
432 .getValue()); 194 .getValue());
433 assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress())); 195 assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress()));
434 196
435 // State machine should have been created by now 197 // State machine should have been created by now
436 198
437 StateMachine stateMachine = 199 StateMachine stateMachine =
438 - StateMachine.lookupStateMachineBySessionId(sessionId); 200 + StateMachine.lookupStateMachineBySessionId(SESSION_ID);
439 assertThat(stateMachine, notNullValue()); 201 assertThat(stateMachine, notNullValue());
440 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); 202 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
441 203
442 // (3) RADIUS MD5 challenge 204 // (3) RADIUS MD5 challenge
443 205
444 - Ethernet radiusCodeAccessChallengePacket = 206 + RADIUS radiusCodeAccessChallengePacket =
445 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5); 207 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
446 - sendPacket(radiusCodeAccessChallengePacket); 208 + aaa.radiusListener.handleRadiusPacket(radiusCodeAccessChallengePacket);
447 209
448 - Ethernet radiusChallengeMD5Packet = fetchPacket(2); 210 + Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
449 - checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5); 211 + checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.ATTR_MD5);
450 212
451 // (4) Supplicant MD5 response 213 // (4) Supplicant MD5 response
452 214
453 - Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5); 215 + Ethernet md5RadiusPacket =
216 + constructSupplicantIdentifyPacket(stateMachine,
217 + EAP.ATTR_MD5,
218 + stateMachine.challengeIdentifier(),
219 + radiusChallengeMD5Packet);
454 sendPacket(md5RadiusPacket); 220 sendPacket(md5RadiusPacket);
455 - Ethernet supplicantMD5ResponsePacket = fetchPacket(3); 221 +
456 - RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket); 222 + RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
457 - assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1)); 223 +
224 + checkRADIUSPacketFromSupplicant(responseMd5RadiusPacket);
225 + assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 0));
458 assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); 226 assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
459 227
460 // State machine should be in pending state 228 // State machine should be in pending state
...@@ -462,37 +230,20 @@ public class AAATest { ...@@ -462,37 +230,20 @@ public class AAATest {
462 assertThat(stateMachine, notNullValue()); 230 assertThat(stateMachine, notNullValue());
463 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); 231 assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
464 232
465 - // (5) RADIUS TLS Challenge 233 + // (5) RADIUS Success
466 -
467 - Ethernet radiusCodeAccessChallengeTLSPacket =
468 - constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS);
469 - sendPacket(radiusCodeAccessChallengeTLSPacket);
470 234
471 - Ethernet radiusChallengeTLSPacket = fetchPacket(4); 235 + RADIUS successPacket =
472 - checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS);
473 -
474 - // (6) Supplicant TLS response
475 -
476 - Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS);
477 - sendPacket(tlsRadiusPacket);
478 - Ethernet supplicantTLSResponsePacket = fetchPacket(5);
479 - RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket);
480 - assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0));
481 - assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
482 -
483 - // (7) RADIUS Success
484 -
485 - Ethernet successPacket =
486 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS); 236 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
487 - sendPacket(successPacket); 237 + aaa.radiusListener.handleRadiusPacket((successPacket));
488 - Ethernet supplicantSuccessPacket = fetchPacket(6); 238 + Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
489 239
490 - checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS); 240 + checkRadiusPacket(aaa, supplicantSuccessPacket, EAP.SUCCESS);
491 241
492 // State machine should be in authorized state 242 // State machine should be in authorized state
493 243
494 assertThat(stateMachine, notNullValue()); 244 assertThat(stateMachine, notNullValue());
495 assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED)); 245 assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
246 +
496 } 247 }
497 248
498 /** 249 /**
...@@ -502,7 +253,7 @@ public class AAATest { ...@@ -502,7 +253,7 @@ public class AAATest {
502 public void testConfig() { 253 public void testConfig() {
503 assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP)); 254 assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP));
504 assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC)); 255 assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC));
505 - assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP)); 256 + assertThat(aaa.radiusIpAddress.getHostAddress(), is(BAD_IP_ADDRESS));
506 assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC)); 257 assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC));
507 } 258 }
508 } 259 }
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 + */
16 +package org.onosproject.aaa;
17 +
18 +import java.nio.ByteBuffer;
19 +import java.security.MessageDigest;
20 +import java.util.LinkedList;
21 +import java.util.List;
22 +
23 +import org.onlab.packet.BasePacket;
24 +import org.onlab.packet.EAP;
25 +import org.onlab.packet.EAPOL;
26 +import org.onlab.packet.EthType;
27 +import org.onlab.packet.Ethernet;
28 +import org.onlab.packet.MacAddress;
29 +import org.onosproject.net.packet.DefaultInboundPacket;
30 +import org.onosproject.net.packet.DefaultPacketContext;
31 +import org.onosproject.net.packet.InboundPacket;
32 +import org.onosproject.net.packet.OutboundPacket;
33 +import org.onosproject.net.packet.PacketContext;
34 +import org.onosproject.net.packet.PacketProcessor;
35 +import org.onosproject.net.packet.PacketServiceAdapter;
36 +
37 +import static org.hamcrest.Matchers.instanceOf;
38 +import static org.hamcrest.Matchers.is;
39 +import static org.hamcrest.Matchers.notNullValue;
40 +import static org.junit.Assert.assertThat;
41 +import static org.junit.Assert.fail;
42 +import static org.onosproject.net.NetTestTools.connectPoint;
43 +
44 +/**
45 + * Common methods for AAA app testing.
46 + */
47 +public class AAATestBase {
48 +
49 + MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
50 + MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
51 +
52 + // Our session id will be the device ID ("of:1") with the port ("1") concatenated
53 + static final String SESSION_ID = "of:11";
54 +
55 + List<BasePacket> savedPackets = new LinkedList<>();
56 + PacketProcessor packetProcessor;
57 +
58 + /**
59 + * Saves the given packet onto the saved packets list.
60 + *
61 + * @param packet packet to save
62 + */
63 + void savePacket(BasePacket packet) {
64 + savedPackets.add(packet);
65 + }
66 +
67 + /**
68 + * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
69 + */
70 + class MockPacketService extends PacketServiceAdapter {
71 +
72 + @Override
73 + public void addProcessor(PacketProcessor processor, int priority) {
74 + packetProcessor = processor;
75 + }
76 +
77 + @Override
78 + public void emit(OutboundPacket packet) {
79 + try {
80 + Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
81 + 0, packet.data().array().length);
82 + savePacket(eth);
83 + } catch (Exception e) {
84 + fail(e.getMessage());
85 + }
86 + }
87 + }
88 +
89 + /**
90 + * Mocks the DefaultPacketContext.
91 + */
92 + final class TestPacketContext extends DefaultPacketContext {
93 +
94 + private TestPacketContext(long time, InboundPacket inPkt,
95 + OutboundPacket outPkt, boolean block) {
96 + super(time, inPkt, outPkt, block);
97 + }
98 +
99 + @Override
100 + public void send() {
101 + // We don't send anything out.
102 + }
103 + }
104 +
105 + /**
106 + * Sends an Ethernet packet to the process method of the Packet Processor.
107 + *
108 + * @param reply Ethernet packet
109 + */
110 + void sendPacket(Ethernet reply) {
111 + final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
112 + InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
113 + reply,
114 + byteBuffer);
115 +
116 + PacketContext context = new TestPacketContext(127L, inPacket, null, false);
117 + packetProcessor.process(context);
118 + }
119 +
120 + /**
121 + * Constructs an Ethernet packet containing identification payload.
122 + *
123 + * @return Ethernet packet
124 + */
125 + Ethernet constructSupplicantIdentifyPacket(StateMachine stateMachine,
126 + byte type,
127 + byte id,
128 + Ethernet radiusChallenge)
129 + throws Exception {
130 + Ethernet eth = new Ethernet();
131 + eth.setDestinationMACAddress(clientMac.toBytes());
132 + eth.setSourceMACAddress(serverMac.toBytes());
133 + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
134 + eth.setVlanID((short) 2);
135 +
136 + String username = "testuser";
137 + byte[] data = username.getBytes();
138 +
139 +
140 + if (type == EAP.ATTR_MD5) {
141 + String password = "testpassword";
142 + EAPOL eapol = (EAPOL) radiusChallenge.getPayload();
143 + EAP eap = (EAP) eapol.getPayload();
144 +
145 + byte[] identifier = new byte[password.length() + eap.getData().length];
146 +
147 + identifier[0] = stateMachine.challengeIdentifier();
148 + System.arraycopy(password.getBytes(), 0, identifier, 1, password.length());
149 + System.arraycopy(eap.getData(), 1, identifier, 1 + password.length(), 16);
150 +
151 + MessageDigest md = MessageDigest.getInstance("MD5");
152 + byte[] hash = md.digest(identifier);
153 + data = new byte[17];
154 + data[0] = (byte) 16;
155 + System.arraycopy(hash, 0, data, 1, 16);
156 + }
157 + EAP eap = new EAP(EAP.RESPONSE, (byte) 1, type,
158 + data);
159 + eap.setIdentifier(id);
160 +
161 + // eapol header
162 + EAPOL eapol = new EAPOL();
163 + eapol.setEapolType(EAPOL.EAPOL_PACKET);
164 + eapol.setPacketLength(eap.getLength());
165 +
166 + // eap part
167 + eapol.setPayload(eap);
168 +
169 + eth.setPayload(eapol);
170 + eth.setPad(true);
171 + return eth;
172 + }
173 +
174 + /**
175 + * Constructs an Ethernet packet containing a EAPOL_START Payload.
176 + *
177 + * @return Ethernet packet
178 + */
179 + Ethernet constructSupplicantStartPacket() {
180 + Ethernet eth = new Ethernet();
181 + eth.setDestinationMACAddress(clientMac.toBytes());
182 + eth.setSourceMACAddress(serverMac.toBytes());
183 + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
184 + eth.setVlanID((short) 2);
185 +
186 + EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 2, EAPOL.EAPOL_START, null);
187 +
188 + // eapol header
189 + EAPOL eapol = new EAPOL();
190 + eapol.setEapolType(EAPOL.EAPOL_START);
191 + eapol.setPacketLength(eap.getLength());
192 +
193 + // eap part
194 + eapol.setPayload(eap);
195 +
196 + eth.setPayload(eapol);
197 + eth.setPad(true);
198 + return eth;
199 + }
200 +
201 + /**
202 + * Checks the contents of a RADIUS packet being sent to the RADIUS server.
203 + *
204 + * @param radiusPacket packet to check
205 + * @param code expected code
206 + */
207 + void checkRadiusPacket(AAA aaa, Ethernet radiusPacket, byte code) {
208 +
209 + assertThat(radiusPacket.getSourceMAC(),
210 + is(MacAddress.valueOf(aaa.nasMacAddress)));
211 + assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
212 +
213 + assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
214 + EAPOL eapol = (EAPOL) radiusPacket.getPayload();
215 + assertThat(eapol, notNullValue());
216 +
217 + assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
218 + assertThat(eapol.getPayload(), instanceOf(EAP.class));
219 + EAP eap = (EAP) eapol.getPayload();
220 + assertThat(eap, notNullValue());
221 +
222 + assertThat(eap.getCode(), is(code));
223 + }
224 +}
...@@ -21,7 +21,9 @@ import org.junit.After; ...@@ -21,7 +21,9 @@ import org.junit.After;
21 import org.junit.Assert; 21 import org.junit.Assert;
22 import org.junit.Before; 22 import org.junit.Before;
23 import org.junit.Test; 23 import org.junit.Test;
24 -import static org.junit.Assert.*; 24 +
25 +import static org.junit.Assert.assertEquals;
26 +import static org.junit.Assert.assertNull;
25 27
26 28
27 public class StateMachineTest { 29 public class StateMachineTest {
......