blob: 120f14f13b6df64132262ec78d340085b23620bf [file] [log] [blame]
Ari Saha89831742015-06-26 10:31:48 -07001/*
Amit Ghoshc9ac1e52017-07-28 12:31:18 +01002 * Copyright 2017-present Open Networking Foundation
Ari Saha89831742015-06-26 10:31:48 -07003 *
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 */
alshabib6d527452016-06-01 18:00:47 -070016package org.opencord.aaa;
Ari Saha89831742015-06-26 10:31:48 -070017
Jonathan Hart932bedc2018-07-12 13:46:09 -070018import org.apache.commons.lang3.builder.ToStringBuilder;
Ari Saha89831742015-06-26 10:31:48 -070019import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Ari Saha89831742015-06-26 10:31:48 -070021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart5db44532018-07-12 18:13:54 -070023import org.apache.felix.scr.annotations.Service;
Jonathan Hart4731dd92018-05-02 17:30:05 -070024import org.onlab.packet.DeserializationException;
Jonathan Harta46dddf2015-06-30 15:31:20 -070025import org.onlab.packet.EAP;
26import org.onlab.packet.EAPOL;
27import org.onlab.packet.EthType;
Ari Saha89831742015-06-26 10:31:48 -070028import org.onlab.packet.Ethernet;
Ari Saha89831742015-06-26 10:31:48 -070029import org.onlab.packet.MacAddress;
Jonathan Harta46dddf2015-06-30 15:31:20 -070030import org.onlab.packet.RADIUS;
31import org.onlab.packet.RADIUSAttribute;
Ari Saha89831742015-06-26 10:31:48 -070032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Jonathan Hart5db44532018-07-12 18:13:54 -070034import org.onosproject.event.AbstractListenerManager;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010035import org.onosproject.mastership.MastershipService;
Ari Saha89831742015-06-26 10:31:48 -070036import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.DeviceId;
Ari Saha89831742015-06-26 10:31:48 -070038import org.onosproject.net.PortNumber;
Ray Milkeyfcb623d2015-10-01 16:48:18 -070039import org.onosproject.net.config.ConfigFactory;
40import org.onosproject.net.config.NetworkConfigEvent;
41import org.onosproject.net.config.NetworkConfigListener;
42import org.onosproject.net.config.NetworkConfigRegistry;
Amit Ghoshf739be52017-09-21 15:49:37 +010043import org.onosproject.net.device.DeviceEvent;
44import org.onosproject.net.device.DeviceListener;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010045import org.onosproject.net.device.DeviceService;
Ari Saha89831742015-06-26 10:31:48 -070046import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070047import org.onosproject.net.flow.TrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070048import org.onosproject.net.packet.DefaultOutboundPacket;
49import org.onosproject.net.packet.InboundPacket;
50import org.onosproject.net.packet.OutboundPacket;
51import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070052import org.onosproject.net.packet.PacketProcessor;
53import org.onosproject.net.packet.PacketService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010054import org.opencord.sadis.SubscriberAndDeviceInformationService;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +053055import org.osgi.service.component.annotations.Activate;
Ari Saha89831742015-06-26 10:31:48 -070056import org.slf4j.Logger;
57
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010058import java.net.InetAddress;
59import java.nio.ByteBuffer;
Amit Ghoshf739be52017-09-21 15:49:37 +010060import java.util.Map;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010061
Deepa Vaddireddye0e10722017-09-27 05:00:10 +053062import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
63import static org.slf4j.LoggerFactory.getLogger;
64
Ari Saha89831742015-06-26 10:31:48 -070065/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070066 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070067 */
Jonathan Hart5db44532018-07-12 18:13:54 -070068@Service
Ari Saha89831742015-06-26 10:31:48 -070069@Component(immediate = true)
Jonathan Hart5db44532018-07-12 18:13:54 -070070public class AaaManager
71 extends AbstractListenerManager<AuthenticationEvent, AuthenticationEventListener>
72 implements AuthenticationService {
73
Charles Chandf7ff862017-01-20 11:22:05 -080074 private static final String APP_NAME = "org.opencord.aaa";
Ray Milkeyf51eba22015-09-25 10:24:23 -070075
Ray Milkeyf61a24e2015-09-24 16:34:02 -070076 private final Logger log = getLogger(getClass());
Ray Milkeyf51eba22015-09-25 10:24:23 -070077
Ray Milkeyf61a24e2015-09-24 16:34:02 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected CoreService coreService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070080
Ray Milkeyf61a24e2015-09-24 16:34:02 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected PacketService packetService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070083
Ray Milkeyf61a24e2015-09-24 16:34:02 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkeyfcb623d2015-10-01 16:48:18 -070085 protected NetworkConfigRegistry netCfgService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070086
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected DeviceService deviceService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected SubscriberAndDeviceInformationService subsService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected MastershipService mastershipService;
95
Amit Ghoshf739be52017-09-21 15:49:37 +010096 private final DeviceListener deviceListener = new InternalDeviceListener();
97
Ray Milkeyfcb623d2015-10-01 16:48:18 -070098 // NAS IP address
99 protected InetAddress nasIpAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100100
101 // self MAC address
Jonathan Hart5db44532018-07-12 18:13:54 -0700102 protected String nasMacAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100103
104 // Parsed RADIUS server addresses
105 protected InetAddress radiusIpAddress;
106
107 // MAC address of RADIUS server or net hop router
108 protected String radiusMacAddress;
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700109
110 // RADIUS server secret
111 protected String radiusSecret;
112
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100113 // bindings
114 protected CustomizationInfo customInfo;
Ray Milkey5d99bd12015-10-06 15:41:30 -0700115
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700116 // our application-specific event handler
117 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
Ray Milkeyf51eba22015-09-25 10:24:23 -0700118
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700119 // our unique identifier
120 private ApplicationId appId;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700121
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100122 // Setup specific customization/attributes on the RADIUS packets
123 PacketCustomizer pktCustomizer;
Ray Milkey967776a2015-10-07 14:37:17 -0700124
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100125 // packet customizer to use
126 private String customizer;
127
128 // Type of connection to use to communicate with Radius server, options are
129 // "socket" or "packet_out"
130 private String radiusConnectionType;
131
Jonathan Hart5db44532018-07-12 18:13:54 -0700132 // Object for the specific type of communication with the RADIUS
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100133 // server, socket based or packet_out based
134 RadiusCommunicator impl = null;
135
136 // latest configuration
137 AaaConfig newCfg;
Ray Milkey967776a2015-10-07 14:37:17 -0700138
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700139 // Configuration properties factory
140 private final ConfigFactory factory =
Jonathan Hart092dfb22015-11-16 23:05:21 -0800141 new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
142 AaaConfig.class,
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700143 "AAA") {
144 @Override
Jonathan Hart092dfb22015-11-16 23:05:21 -0800145 public AaaConfig createConfig() {
146 return new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700147 }
148 };
Ray Milkeyf51eba22015-09-25 10:24:23 -0700149
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700150 // Listener for config changes
151 private final InternalConfigListener cfgListener = new InternalConfigListener();
Ari Saha89831742015-06-26 10:31:48 -0700152
Jonathan Hart5db44532018-07-12 18:13:54 -0700153 private StateMachineDelegate delegate = new InternalStateMachineDelegate();
154
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700155 /**
156 * Builds an EAPOL packet based on the given parameters.
157 *
158 * @param dstMac destination MAC address
159 * @param srcMac source MAC address
160 * @param vlan vlan identifier
161 * @param eapolType EAPOL type
162 * @param eap EAP payload
163 * @return Ethernet frame
164 */
165 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100166 short vlan, byte eapolType, EAP eap, byte priorityCode) {
Ari Saha89831742015-06-26 10:31:48 -0700167
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700168 Ethernet eth = new Ethernet();
169 eth.setDestinationMACAddress(dstMac.toBytes());
170 eth.setSourceMACAddress(srcMac.toBytes());
171 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
172 if (vlan != Ethernet.VLAN_UNTAGGED) {
173 eth.setVlanID(vlan);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100174 eth.setPriorityCode(priorityCode);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700175 }
176 //eapol header
177 EAPOL eapol = new EAPOL();
178 eapol.setEapolType(eapolType);
179 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700180
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700181 //eap part
182 eapol.setPayload(eap);
183
184 eth.setPayload(eapol);
185 eth.setPad(true);
186 return eth;
187 }
Ari Saha89831742015-06-26 10:31:48 -0700188
Ari Saha89831742015-06-26 10:31:48 -0700189 @Activate
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700190 public void activate() {
Charles Chandf7ff862017-01-20 11:22:05 -0800191 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart5db44532018-07-12 18:13:54 -0700192 eventDispatcher.addSink(AuthenticationEvent.class, listenerRegistry);
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400193 netCfgService.addListener(cfgListener);
194 netCfgService.registerConfigFactory(factory);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530195 customInfo = new CustomizationInfo(subsService, deviceService);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800196 cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400197 log.info("Starting with config {} {}", this, newCfg);
198
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530199 configureRadiusCommunication();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700200
Ari Saha89831742015-06-26 10:31:48 -0700201 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700202 packetService.addProcessor(processor, PacketProcessor.director(2));
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100203
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700204
205 StateMachine.initializeMaps();
Jonathan Hart5db44532018-07-12 18:13:54 -0700206 StateMachine.setDelegate(delegate);
Ari Saha89831742015-06-26 10:31:48 -0700207
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100208 impl.initializeLocalState(newCfg);
alshabib27afc1d2016-12-23 00:33:58 -0800209
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100210 impl.requestIntercepts();
211
Amit Ghoshf739be52017-09-21 15:49:37 +0100212 deviceService.addListener(deviceListener);
213
Jian Li13c67162015-12-09 13:20:34 -0800214 log.info("Started");
Ari Saha89831742015-06-26 10:31:48 -0700215 }
216
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530217
Ari Saha89831742015-06-26 10:31:48 -0700218 @Deactivate
219 public void deactivate() {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100220 impl.withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700221 packetService.removeProcessor(processor);
Deepa Vaddireddyb9c24c62017-09-21 13:45:30 +0530222 netCfgService.removeListener(cfgListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700223 StateMachine.unsetDelegate(delegate);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530224 StateMachine.destroyMaps();
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100225 impl.deactivate();
Amit Ghoshf739be52017-09-21 15:49:37 +0100226 deviceService.removeListener(deviceListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700227 eventDispatcher.removeSink(AuthenticationEvent.class);
Jian Li13c67162015-12-09 13:20:34 -0800228 log.info("Stopped");
Ray Milkey967776a2015-10-07 14:37:17 -0700229 }
230
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530231 private void configureRadiusCommunication() {
232 if (radiusConnectionType.toLowerCase().equals("socket")) {
233 impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
234 } else {
235 impl = new PortBasedRadiusCommunicator(appId, packetService, mastershipService,
236 deviceService, subsService, pktCustomizer, this);
237 }
238 }
239
240 private void configurePacketCustomizer() {
241 switch (customizer.toLowerCase()) {
242 case "sample":
243 pktCustomizer = new SamplePacketCustomizer(customInfo);
244 log.info("Created SamplePacketCustomizer");
245 break;
246 default:
247 pktCustomizer = new PacketCustomizer(customInfo);
248 log.info("Created default PacketCustomizer");
249 break;
250 }
251 }
252
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100253 /**
254 * Send RADIUS packet to the RADIUS server.
255 *
256 * @param radiusPacket RADIUS packet to be sent to server.
257 * @param inPkt Incoming EAPOL packet
258 */
259 protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
260 impl.sendRadiusPacket(radiusPacket, inPkt);
261 }
Ray Milkey967776a2015-10-07 14:37:17 -0700262
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100263 /**
264 * Handles RADIUS packets.
265 *
266 * @param radiusPacket RADIUS packet coming from the RADIUS server.
267 * @throws StateMachineException if an illegal state transition is triggered
Jonathan Hart4731dd92018-05-02 17:30:05 -0700268 * @throws DeserializationException if packet deserialization fails
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100269 */
Jonathan Hart4731dd92018-05-02 17:30:05 -0700270 public void handleRadiusPacket(RADIUS radiusPacket)
271 throws StateMachineException, DeserializationException {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100272 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
273 if (stateMachine == null) {
274 log.error("Invalid session identifier {}, exiting...", radiusPacket.getIdentifier());
275 return;
Ray Milkey967776a2015-10-07 14:37:17 -0700276 }
Ari Saha89831742015-06-26 10:31:48 -0700277
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100278 EAP eapPayload;
279 Ethernet eth;
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700280
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100281 switch (radiusPacket.getCode()) {
282 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400283 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100284 RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
285 byte[] challengeState = null;
286 if (radiusAttrState != null) {
287 challengeState = radiusAttrState.getValue();
288 }
289 eapPayload = radiusPacket.decapsulateMessage();
290 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
291 eth = buildEapolResponse(stateMachine.supplicantAddress(),
292 MacAddress.valueOf(nasMacAddress),
293 stateMachine.vlanId(),
294 EAPOL.EAPOL_PACKET,
295 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400296 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100297 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
298 break;
299 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400300 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100301 //send an EAPOL - Success to the supplicant.
302 byte[] eapMessageSuccess =
303 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700304 eapPayload = EAP.deserializer().deserialize(
305 eapMessageSuccess, 0, eapMessageSuccess.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100306 eth = buildEapolResponse(stateMachine.supplicantAddress(),
307 MacAddress.valueOf(nasMacAddress),
308 stateMachine.vlanId(),
309 EAPOL.EAPOL_PACKET,
310 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400311 log.info("Send EAP success message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100312 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
313
314 stateMachine.authorizeAccess();
315
316 break;
317 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400318 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100319 //send an EAPOL - Failure to the supplicant.
320 byte[] eapMessageFailure;
321 eapPayload = new EAP();
322 RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
323 if (radiusAttrEap == null) {
324 eapPayload.setCode(EAP.FAILURE);
325 eapPayload.setIdentifier(stateMachine.challengeIdentifier());
326 eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
327 } else {
328 eapMessageFailure = radiusAttrEap.getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700329 eapPayload = EAP.deserializer().deserialize(
330 eapMessageFailure, 0, eapMessageFailure.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100331 }
332 eth = buildEapolResponse(stateMachine.supplicantAddress(),
333 MacAddress.valueOf(nasMacAddress),
334 stateMachine.vlanId(),
335 EAPOL.EAPOL_PACKET,
336 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400337 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100338 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
339 stateMachine.denyAccess();
340
341 break;
342 default:
343 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
344 }
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700345 }
346
Ray Milkey967776a2015-10-07 14:37:17 -0700347 /**
348 * Send the ethernet packet to the supplicant.
349 *
350 * @param ethernetPkt the ethernet packet
351 * @param connectPoint the connect point to send out
352 */
353 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
354 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
355 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
356 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
357 packetService.emit(packet);
358 }
359
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400360 @Override
361 public String toString() {
362 return ToStringBuilder.reflectionToString(this);
363 }
364
Ari Saha89831742015-06-26 10:31:48 -0700365 // our handler defined as a private inner class
366
367 /**
368 * Packet processor responsible for forwarding packets along their paths.
369 */
370 private class ReactivePacketProcessor implements PacketProcessor {
371 @Override
372 public void process(PacketContext context) {
373
374 // Extract the original Ethernet frame from the packet information
375 InboundPacket pkt = context.inPacket();
376 Ethernet ethPkt = pkt.parsed();
377 if (ethPkt == null) {
378 return;
379 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100380
Ray Milkeyf51eba22015-09-25 10:24:23 -0700381 try {
382 // identify if incoming packet comes from supplicant (EAP) or RADIUS
383 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
384 case EAPOL:
385 handleSupplicantPacket(context.inPacket());
386 break;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700387 default:
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100388 // any other packets let the specific implementation handle
389 impl.handlePacketFromServer(context);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700390 }
Ray Milkey967776a2015-10-07 14:37:17 -0700391 } catch (StateMachineException e) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100392 log.warn("Unable to process packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700393 }
394 }
395
Ray Milkey9eb293f2015-09-30 15:09:17 -0700396 /**
397 * Creates and initializes common fields of a RADIUS packet.
398 *
Ray Milkey967776a2015-10-07 14:37:17 -0700399 * @param stateMachine state machine for the request
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700400 * @param eapPacket EAP packet
Ray Milkey9eb293f2015-09-30 15:09:17 -0700401 * @return RADIUS packet
402 */
Ray Milkey967776a2015-10-07 14:37:17 -0700403 private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
Ray Milkey9eb293f2015-09-30 15:09:17 -0700404 RADIUS radiusPayload =
405 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
406 eapPacket.getIdentifier());
Ray Milkey967776a2015-10-07 14:37:17 -0700407
408 // set Request Authenticator in StateMachine
409 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
410
Ray Milkey9eb293f2015-09-30 15:09:17 -0700411 radiusPayload.setIdentifier(identifier);
412 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkey967776a2015-10-07 14:37:17 -0700413 stateMachine.username());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700414
415 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Jonathan Hart092dfb22015-11-16 23:05:21 -0800416 AaaManager.this.nasIpAddress.getAddress());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700417
418 radiusPayload.encapsulateMessage(eapPacket);
Ray Milkey9eb293f2015-09-30 15:09:17 -0700419
420 return radiusPayload;
421 }
Ari Saha89831742015-06-26 10:31:48 -0700422
423 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700424 * Handles PAE packets (supplicant).
425 *
426 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700427 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700428 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700429 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700430 // Where does it come from?
Jonathan Hart092dfb22015-11-16 23:05:21 -0800431 MacAddress srcMac = ethPkt.getSourceMAC();
Ari Saha89831742015-06-26 10:31:48 -0700432
Jonathan Harta46dddf2015-06-30 15:31:20 -0700433 DeviceId deviceId = inPacket.receivedFrom().deviceId();
434 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700435 String sessionId = deviceId.toString() + portNumber.toString();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700436 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
437 if (stateMachine == null) {
Jonathan Hart932bedc2018-07-12 13:46:09 -0700438 stateMachine = new StateMachine(sessionId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100439 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700440
441 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700442
443 switch (eapol.getEapolType()) {
444 case EAPOL.EAPOL_START:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400445 log.debug("EAP packet: EAPOL_START");
Ray Milkeyf51eba22015-09-25 10:24:23 -0700446 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Jonathan Hart5db44532018-07-12 18:13:54 -0700447 stateMachine.start();
Ari Saha89831742015-06-26 10:31:48 -0700448
Ray Milkeyf51eba22015-09-25 10:24:23 -0700449 //send an EAP Request/Identify to the supplicant
450 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100451 if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
452 stateMachine.setPriorityCode(ethPkt.getPriorityCode());
453 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800454 Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
Ray Milkeyf51eba22015-09-25 10:24:23 -0700455 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100456 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400457
Jonathan Hart092dfb22015-11-16 23:05:21 -0800458 stateMachine.setSupplicantAddress(srcMac);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700459 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700460
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400461 log.debug("Getting EAP identity from supplicant {}", stateMachine.supplicantAddress().toString());
Ray Milkey967776a2015-10-07 14:37:17 -0700462 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700463
464 break;
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800465 case EAPOL.EAPOL_LOGOFF:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400466 log.debug("EAP packet: EAPOL_LOGOFF");
Ray Milkeyb34b4962016-01-04 10:24:43 -0800467 if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
468 stateMachine.logoff();
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800469 }
470
471 break;
Ari Saha89831742015-06-26 10:31:48 -0700472 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700473 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700474 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700475 EAP eapPacket = (EAP) eapol.getPayload();
476
477 byte dataType = eapPacket.getDataType();
478 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700479
Ray Milkey9eb293f2015-09-30 15:09:17 -0700480 case EAP.ATTR_IDENTITY:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400481 log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700482 // request id access to RADIUS
483 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700484
Ray Milkey967776a2015-10-07 14:37:17 -0700485 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100486 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800487 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Ari Saha89831742015-06-26 10:31:48 -0700488
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100489 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700490
Ray Milkey9eb293f2015-09-30 15:09:17 -0700491 // change the state to "PENDING"
492 stateMachine.requestAccess();
493 break;
Ari Saha89831742015-06-26 10:31:48 -0700494 case EAP.ATTR_MD5:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400495 log.debug("EAP packet: EAPOL_PACKET ATTR_MD5");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700496 // verify if the EAP identifier corresponds to the
497 // challenge identifier from the client state
498 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700499 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700500 //send the RADIUS challenge response
Ray Milkey967776a2015-10-07 14:37:17 -0700501 radiusPayload =
502 getRadiusPayload(stateMachine,
503 stateMachine.identifier(),
504 eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100505 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700506
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800507 if (stateMachine.challengeState() != null) {
508 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
509 stateMachine.challengeState());
510 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800511 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100512 sendRadiusPacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700513 }
514 break;
515 case EAP.ATTR_TLS:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400516 log.debug("EAP packet: EAPOL_PACKET ATTR_TLS");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700517 // request id access to RADIUS
Ray Milkey967776a2015-10-07 14:37:17 -0700518 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100519 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700520
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800521 if (stateMachine.challengeState() != null) {
522 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
523 stateMachine.challengeState());
524 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700525 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700526
Jonathan Hart092dfb22015-11-16 23:05:21 -0800527 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100528 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkey5493b512015-10-21 12:13:49 -0700529
Ray Milkeyf3790b82015-10-21 16:28:08 -0700530 if (stateMachine.state() != StateMachine.STATE_PENDING) {
531 stateMachine.requestAccess();
532 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700533
Ari Saha89831742015-06-26 10:31:48 -0700534 break;
535 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400536 log.warn("Unknown EAP packet type");
Ari Saha89831742015-06-26 10:31:48 -0700537 return;
538 }
539 break;
540 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400541 log.debug("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700542 }
543 }
Ray Milkey967776a2015-10-07 14:37:17 -0700544 }
545
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100546 /**
Jonathan Hart5db44532018-07-12 18:13:54 -0700547 * Delegate allowing the StateMachine to notify us of events.
548 */
549 private class InternalStateMachineDelegate implements StateMachineDelegate {
550
551 @Override
552 public void notify(AuthenticationEvent authenticationEvent) {
553 log.info("Auth event {} for {}",
554 authenticationEvent.type(), authenticationEvent.subject());
555 post(authenticationEvent);
556 }
557 }
558
559 /**
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100560 * Configuration Listener, handles change in configuration.
561 */
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700562 private class InternalConfigListener implements NetworkConfigListener {
563
564 /**
Ray Milkeyc9e8dcc2015-12-30 10:31:32 -0800565 * Reconfigures the AAA application according to the
566 * configuration parameters passed.
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700567 *
568 * @param cfg configuration object
569 */
Jonathan Hart092dfb22015-11-16 23:05:21 -0800570 private void reconfigureNetwork(AaaConfig cfg) {
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400571 log.info("Reconfiguring AaaConfig from config: {}", cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100572
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700573 if (cfg == null) {
Jonathan Hart092dfb22015-11-16 23:05:21 -0800574 newCfg = new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700575 } else {
576 newCfg = cfg;
577 }
578 if (newCfg.nasIp() != null) {
579 nasIpAddress = newCfg.nasIp();
580 }
581 if (newCfg.radiusIp() != null) {
582 radiusIpAddress = newCfg.radiusIp();
583 }
584 if (newCfg.radiusMac() != null) {
585 radiusMacAddress = newCfg.radiusMac();
586 }
587 if (newCfg.nasMac() != null) {
588 nasMacAddress = newCfg.nasMac();
589 }
590 if (newCfg.radiusSecret() != null) {
591 radiusSecret = newCfg.radiusSecret();
592 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100593
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530594 boolean reconfigureCustomizer = false;
595 if (customizer == null || !customizer.equals(newCfg.radiusPktCustomizer())) {
596 customizer = newCfg.radiusPktCustomizer();
597 configurePacketCustomizer();
598 reconfigureCustomizer = true;
599 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100600
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530601 if (radiusConnectionType == null
602 || reconfigureCustomizer
603 || !radiusConnectionType.equals(newCfg.radiusConnectionType())) {
604 radiusConnectionType = newCfg.radiusConnectionType();
605 if (impl != null) {
606 impl.withdrawIntercepts();
607 impl.clearLocalState();
608 }
609 configureRadiusCommunication();
610 impl.initializeLocalState(newCfg);
611 impl.requestIntercepts();
612 } else if (impl != null) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100613 impl.clearLocalState();
614 impl.initializeLocalState(newCfg);
Ray Milkey5d99bd12015-10-06 15:41:30 -0700615 }
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700616 }
617
618 @Override
619 public void event(NetworkConfigEvent event) {
620
621 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
622 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
Jonathan Hart092dfb22015-11-16 23:05:21 -0800623 event.configClass().equals(AaaConfig.class)) {
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700624
Jonathan Hart092dfb22015-11-16 23:05:21 -0800625 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700626 reconfigureNetwork(cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100627
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400628 log.info("Reconfigured: {}", cfg.toString());
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700629 }
630 }
631 }
Amit Ghoshf739be52017-09-21 15:49:37 +0100632
633 private class InternalDeviceListener implements DeviceListener {
634 @Override
635 public void event(DeviceEvent event) {
636
637 switch (event.type()) {
638 case PORT_REMOVED:
639 DeviceId devId = event.subject().id();
640 PortNumber portNumber = event.port().number();
641 String sessionId = devId.toString() + portNumber.toString();
642
643 Map<String, StateMachine> sessionIdMap = StateMachine.sessionIdMap();
644 StateMachine removed = sessionIdMap.remove(sessionId);
645 if (removed != null) {
646 StateMachine.deleteStateMachineMapping(removed);
647 }
648
649 break;
650 default:
651 return;
652 }
653 }
654 }
Ari Saha89831742015-06-26 10:31:48 -0700655}