blob: 59784bb45ed8628c933d5717fbd057081a877e70 [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
Saurav Das987441a2018-09-18 16:33:47 -070018import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import java.net.InetAddress;
22import java.nio.ByteBuffer;
23import java.util.Map;
24
Jonathan Hart932bedc2018-07-12 13:46:09 -070025import org.apache.commons.lang3.builder.ToStringBuilder;
Ari Saha89831742015-06-26 10:31:48 -070026import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
Ari Saha89831742015-06-26 10:31:48 -070028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart5db44532018-07-12 18:13:54 -070030import org.apache.felix.scr.annotations.Service;
Jonathan Hart4731dd92018-05-02 17:30:05 -070031import org.onlab.packet.DeserializationException;
Jonathan Harta46dddf2015-06-30 15:31:20 -070032import org.onlab.packet.EAP;
33import org.onlab.packet.EAPOL;
34import org.onlab.packet.EthType;
Ari Saha89831742015-06-26 10:31:48 -070035import org.onlab.packet.Ethernet;
Ari Saha89831742015-06-26 10:31:48 -070036import org.onlab.packet.MacAddress;
Jonathan Harta46dddf2015-06-30 15:31:20 -070037import org.onlab.packet.RADIUS;
38import org.onlab.packet.RADIUSAttribute;
Ari Saha89831742015-06-26 10:31:48 -070039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Jonathan Hart5db44532018-07-12 18:13:54 -070041import org.onosproject.event.AbstractListenerManager;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010042import org.onosproject.mastership.MastershipService;
Ari Saha89831742015-06-26 10:31:48 -070043import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.DeviceId;
Ari Saha89831742015-06-26 10:31:48 -070045import org.onosproject.net.PortNumber;
Ray Milkeyfcb623d2015-10-01 16:48:18 -070046import org.onosproject.net.config.ConfigFactory;
47import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
Amit Ghoshf739be52017-09-21 15:49:37 +010050import org.onosproject.net.device.DeviceEvent;
51import org.onosproject.net.device.DeviceListener;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010052import org.onosproject.net.device.DeviceService;
Ari Saha89831742015-06-26 10:31:48 -070053import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070054import org.onosproject.net.flow.TrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070055import org.onosproject.net.packet.DefaultOutboundPacket;
56import org.onosproject.net.packet.InboundPacket;
57import org.onosproject.net.packet.OutboundPacket;
58import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070059import org.onosproject.net.packet.PacketProcessor;
60import org.onosproject.net.packet.PacketService;
Gamze Abaka1cfdb192018-10-25 11:39:19 +000061import org.opencord.sadis.BaseInformationService;
62import org.opencord.sadis.SadisService;
63import org.opencord.sadis.SubscriberAndDeviceInformation;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +053064import org.osgi.service.component.annotations.Activate;
Ari Saha89831742015-06-26 10:31:48 -070065import org.slf4j.Logger;
66
Ari Saha89831742015-06-26 10:31:48 -070067/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070068 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070069 */
Jonathan Hart5db44532018-07-12 18:13:54 -070070@Service
Ari Saha89831742015-06-26 10:31:48 -070071@Component(immediate = true)
Jonathan Hart5db44532018-07-12 18:13:54 -070072public class AaaManager
73 extends AbstractListenerManager<AuthenticationEvent, AuthenticationEventListener>
74 implements AuthenticationService {
75
Charles Chandf7ff862017-01-20 11:22:05 -080076 private static final String APP_NAME = "org.opencord.aaa";
Ray Milkeyf51eba22015-09-25 10:24:23 -070077
Ray Milkeyf61a24e2015-09-24 16:34:02 -070078 private final Logger log = getLogger(getClass());
Ray Milkeyf51eba22015-09-25 10:24:23 -070079
Ray Milkeyf61a24e2015-09-24 16:34:02 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected CoreService coreService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070082
Ray Milkeyf61a24e2015-09-24 16:34:02 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected PacketService packetService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070085
Ray Milkeyf61a24e2015-09-24 16:34:02 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkeyfcb623d2015-10-01 16:48:18 -070087 protected NetworkConfigRegistry netCfgService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070088
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected DeviceService deviceService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Gamze Abaka1cfdb192018-10-25 11:39:19 +000093 protected SadisService sadisService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010094
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected MastershipService mastershipService;
97
Gamze Abaka1cfdb192018-10-25 11:39:19 +000098 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
99
Amit Ghoshf739be52017-09-21 15:49:37 +0100100 private final DeviceListener deviceListener = new InternalDeviceListener();
101
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700102 // NAS IP address
103 protected InetAddress nasIpAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100104
105 // self MAC address
Jonathan Hart5db44532018-07-12 18:13:54 -0700106 protected String nasMacAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100107
108 // Parsed RADIUS server addresses
109 protected InetAddress radiusIpAddress;
110
111 // MAC address of RADIUS server or net hop router
112 protected String radiusMacAddress;
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700113
114 // RADIUS server secret
115 protected String radiusSecret;
116
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100117 // bindings
118 protected CustomizationInfo customInfo;
Ray Milkey5d99bd12015-10-06 15:41:30 -0700119
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700120 // our application-specific event handler
121 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
Ray Milkeyf51eba22015-09-25 10:24:23 -0700122
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700123 // our unique identifier
124 private ApplicationId appId;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700125
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100126 // Setup specific customization/attributes on the RADIUS packets
127 PacketCustomizer pktCustomizer;
Ray Milkey967776a2015-10-07 14:37:17 -0700128
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100129 // packet customizer to use
130 private String customizer;
131
132 // Type of connection to use to communicate with Radius server, options are
133 // "socket" or "packet_out"
134 private String radiusConnectionType;
135
Jonathan Hart5db44532018-07-12 18:13:54 -0700136 // Object for the specific type of communication with the RADIUS
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100137 // server, socket based or packet_out based
138 RadiusCommunicator impl = null;
139
140 // latest configuration
141 AaaConfig newCfg;
Ray Milkey967776a2015-10-07 14:37:17 -0700142
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700143 // Configuration properties factory
144 private final ConfigFactory factory =
Jonathan Hart092dfb22015-11-16 23:05:21 -0800145 new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
146 AaaConfig.class,
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700147 "AAA") {
148 @Override
Jonathan Hart092dfb22015-11-16 23:05:21 -0800149 public AaaConfig createConfig() {
150 return new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700151 }
152 };
Ray Milkeyf51eba22015-09-25 10:24:23 -0700153
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700154 // Listener for config changes
155 private final InternalConfigListener cfgListener = new InternalConfigListener();
Ari Saha89831742015-06-26 10:31:48 -0700156
Jonathan Hart5db44532018-07-12 18:13:54 -0700157 private StateMachineDelegate delegate = new InternalStateMachineDelegate();
158
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700159 /**
160 * Builds an EAPOL packet based on the given parameters.
161 *
162 * @param dstMac destination MAC address
163 * @param srcMac source MAC address
164 * @param vlan vlan identifier
165 * @param eapolType EAPOL type
166 * @param eap EAP payload
167 * @return Ethernet frame
168 */
169 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100170 short vlan, byte eapolType, EAP eap, byte priorityCode) {
Ari Saha89831742015-06-26 10:31:48 -0700171
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700172 Ethernet eth = new Ethernet();
173 eth.setDestinationMACAddress(dstMac.toBytes());
174 eth.setSourceMACAddress(srcMac.toBytes());
175 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
176 if (vlan != Ethernet.VLAN_UNTAGGED) {
177 eth.setVlanID(vlan);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100178 eth.setPriorityCode(priorityCode);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700179 }
180 //eapol header
181 EAPOL eapol = new EAPOL();
182 eapol.setEapolType(eapolType);
183 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700184
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700185 //eap part
186 eapol.setPayload(eap);
187
188 eth.setPayload(eapol);
189 eth.setPad(true);
190 return eth;
191 }
Ari Saha89831742015-06-26 10:31:48 -0700192
Ari Saha89831742015-06-26 10:31:48 -0700193 @Activate
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700194 public void activate() {
Charles Chandf7ff862017-01-20 11:22:05 -0800195 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart5db44532018-07-12 18:13:54 -0700196 eventDispatcher.addSink(AuthenticationEvent.class, listenerRegistry);
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400197 netCfgService.addListener(cfgListener);
198 netCfgService.registerConfigFactory(factory);
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000199 subsService = sadisService.getSubscriberInfoService();
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530200 customInfo = new CustomizationInfo(subsService, deviceService);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800201 cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400202 log.info("Starting with config {} {}", this, newCfg);
203
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530204 configureRadiusCommunication();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700205
Ari Saha89831742015-06-26 10:31:48 -0700206 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700207 packetService.addProcessor(processor, PacketProcessor.director(2));
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100208
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700209
210 StateMachine.initializeMaps();
Jonathan Hart5db44532018-07-12 18:13:54 -0700211 StateMachine.setDelegate(delegate);
Ari Saha89831742015-06-26 10:31:48 -0700212
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100213 impl.initializeLocalState(newCfg);
alshabib27afc1d2016-12-23 00:33:58 -0800214
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100215 impl.requestIntercepts();
216
Amit Ghoshf739be52017-09-21 15:49:37 +0100217 deviceService.addListener(deviceListener);
218
Jian Li13c67162015-12-09 13:20:34 -0800219 log.info("Started");
Ari Saha89831742015-06-26 10:31:48 -0700220 }
221
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530222
Ari Saha89831742015-06-26 10:31:48 -0700223 @Deactivate
224 public void deactivate() {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100225 impl.withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700226 packetService.removeProcessor(processor);
Deepa Vaddireddyb9c24c62017-09-21 13:45:30 +0530227 netCfgService.removeListener(cfgListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700228 StateMachine.unsetDelegate(delegate);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530229 StateMachine.destroyMaps();
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100230 impl.deactivate();
Amit Ghoshf739be52017-09-21 15:49:37 +0100231 deviceService.removeListener(deviceListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700232 eventDispatcher.removeSink(AuthenticationEvent.class);
Jian Li13c67162015-12-09 13:20:34 -0800233 log.info("Stopped");
Ray Milkey967776a2015-10-07 14:37:17 -0700234 }
235
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530236 private void configureRadiusCommunication() {
237 if (radiusConnectionType.toLowerCase().equals("socket")) {
238 impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
239 } else {
240 impl = new PortBasedRadiusCommunicator(appId, packetService, mastershipService,
241 deviceService, subsService, pktCustomizer, this);
242 }
243 }
244
245 private void configurePacketCustomizer() {
246 switch (customizer.toLowerCase()) {
247 case "sample":
248 pktCustomizer = new SamplePacketCustomizer(customInfo);
249 log.info("Created SamplePacketCustomizer");
250 break;
Saurav Dase72358a2018-11-13 21:56:46 -0800251 case "att":
252 pktCustomizer = new AttPacketCustomizer(customInfo);
253 log.info("Created AttPacketCustomizer");
254 break;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530255 default:
256 pktCustomizer = new PacketCustomizer(customInfo);
257 log.info("Created default PacketCustomizer");
258 break;
259 }
260 }
261
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100262 /**
263 * Send RADIUS packet to the RADIUS server.
264 *
265 * @param radiusPacket RADIUS packet to be sent to server.
266 * @param inPkt Incoming EAPOL packet
267 */
268 protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
269 impl.sendRadiusPacket(radiusPacket, inPkt);
270 }
Ray Milkey967776a2015-10-07 14:37:17 -0700271
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100272 /**
273 * Handles RADIUS packets.
274 *
275 * @param radiusPacket RADIUS packet coming from the RADIUS server.
276 * @throws StateMachineException if an illegal state transition is triggered
Jonathan Hart4731dd92018-05-02 17:30:05 -0700277 * @throws DeserializationException if packet deserialization fails
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100278 */
Jonathan Hart4731dd92018-05-02 17:30:05 -0700279 public void handleRadiusPacket(RADIUS radiusPacket)
280 throws StateMachineException, DeserializationException {
Saurav Das987441a2018-09-18 16:33:47 -0700281 if (log.isTraceEnabled()) {
282 log.trace("Received RADIUS packet {}", radiusPacket);
283 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100284 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
285 if (stateMachine == null) {
Saurav Das987441a2018-09-18 16:33:47 -0700286 log.error("Invalid packet identifier {}, could not find corresponding "
287 + "state machine ... exiting", radiusPacket.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100288 return;
Ray Milkey967776a2015-10-07 14:37:17 -0700289 }
Ari Saha89831742015-06-26 10:31:48 -0700290
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100291 EAP eapPayload;
292 Ethernet eth;
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700293
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100294 switch (radiusPacket.getCode()) {
295 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400296 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100297 RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
298 byte[] challengeState = null;
299 if (radiusAttrState != null) {
300 challengeState = radiusAttrState.getValue();
301 }
302 eapPayload = radiusPacket.decapsulateMessage();
303 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
304 eth = buildEapolResponse(stateMachine.supplicantAddress(),
305 MacAddress.valueOf(nasMacAddress),
306 stateMachine.vlanId(),
307 EAPOL.EAPOL_PACKET,
308 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400309 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100310 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
311 break;
312 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400313 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100314 //send an EAPOL - Success to the supplicant.
315 byte[] eapMessageSuccess =
316 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700317 eapPayload = EAP.deserializer().deserialize(
318 eapMessageSuccess, 0, eapMessageSuccess.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100319 eth = buildEapolResponse(stateMachine.supplicantAddress(),
320 MacAddress.valueOf(nasMacAddress),
321 stateMachine.vlanId(),
322 EAPOL.EAPOL_PACKET,
323 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400324 log.info("Send EAP success message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100325 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
326
327 stateMachine.authorizeAccess();
328
329 break;
330 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400331 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100332 //send an EAPOL - Failure to the supplicant.
333 byte[] eapMessageFailure;
334 eapPayload = new EAP();
335 RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
336 if (radiusAttrEap == null) {
337 eapPayload.setCode(EAP.FAILURE);
338 eapPayload.setIdentifier(stateMachine.challengeIdentifier());
339 eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
340 } else {
341 eapMessageFailure = radiusAttrEap.getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700342 eapPayload = EAP.deserializer().deserialize(
343 eapMessageFailure, 0, eapMessageFailure.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100344 }
345 eth = buildEapolResponse(stateMachine.supplicantAddress(),
346 MacAddress.valueOf(nasMacAddress),
347 stateMachine.vlanId(),
348 EAPOL.EAPOL_PACKET,
349 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400350 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100351 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
352 stateMachine.denyAccess();
353
354 break;
355 default:
356 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
357 }
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700358 }
359
Ray Milkey967776a2015-10-07 14:37:17 -0700360 /**
361 * Send the ethernet packet to the supplicant.
362 *
363 * @param ethernetPkt the ethernet packet
364 * @param connectPoint the connect point to send out
365 */
366 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
367 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
368 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
369 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Saurav Das987441a2018-09-18 16:33:47 -0700370 if (log.isTraceEnabled()) {
371 EAPOL eap = ((EAPOL) ethernetPkt.getPayload());
372 log.trace("Sending eapol payload {} enclosed in {} to supplicant at {}",
373 eap, ethernetPkt, connectPoint);
374 }
Ray Milkey967776a2015-10-07 14:37:17 -0700375 packetService.emit(packet);
376 }
377
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400378 @Override
379 public String toString() {
380 return ToStringBuilder.reflectionToString(this);
381 }
382
Ari Saha89831742015-06-26 10:31:48 -0700383 // our handler defined as a private inner class
384
385 /**
386 * Packet processor responsible for forwarding packets along their paths.
387 */
388 private class ReactivePacketProcessor implements PacketProcessor {
389 @Override
390 public void process(PacketContext context) {
391
392 // Extract the original Ethernet frame from the packet information
393 InboundPacket pkt = context.inPacket();
394 Ethernet ethPkt = pkt.parsed();
395 if (ethPkt == null) {
396 return;
397 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100398
Ray Milkeyf51eba22015-09-25 10:24:23 -0700399 try {
400 // identify if incoming packet comes from supplicant (EAP) or RADIUS
401 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
402 case EAPOL:
403 handleSupplicantPacket(context.inPacket());
404 break;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700405 default:
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100406 // any other packets let the specific implementation handle
407 impl.handlePacketFromServer(context);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700408 }
Ray Milkey967776a2015-10-07 14:37:17 -0700409 } catch (StateMachineException e) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100410 log.warn("Unable to process packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700411 }
412 }
413
Ray Milkey9eb293f2015-09-30 15:09:17 -0700414 /**
415 * Creates and initializes common fields of a RADIUS packet.
416 *
Ray Milkey967776a2015-10-07 14:37:17 -0700417 * @param stateMachine state machine for the request
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700418 * @param eapPacket EAP packet
Ray Milkey9eb293f2015-09-30 15:09:17 -0700419 * @return RADIUS packet
420 */
Ray Milkey967776a2015-10-07 14:37:17 -0700421 private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
Ray Milkey9eb293f2015-09-30 15:09:17 -0700422 RADIUS radiusPayload =
423 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
424 eapPacket.getIdentifier());
Ray Milkey967776a2015-10-07 14:37:17 -0700425
426 // set Request Authenticator in StateMachine
427 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
428
Ray Milkey9eb293f2015-09-30 15:09:17 -0700429 radiusPayload.setIdentifier(identifier);
430 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkey967776a2015-10-07 14:37:17 -0700431 stateMachine.username());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700432
433 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Jonathan Hart092dfb22015-11-16 23:05:21 -0800434 AaaManager.this.nasIpAddress.getAddress());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700435
436 radiusPayload.encapsulateMessage(eapPacket);
Ray Milkey9eb293f2015-09-30 15:09:17 -0700437
438 return radiusPayload;
439 }
Ari Saha89831742015-06-26 10:31:48 -0700440
441 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700442 * Handles PAE packets (supplicant).
443 *
444 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700445 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700446 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700447 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700448 // Where does it come from?
Jonathan Hart092dfb22015-11-16 23:05:21 -0800449 MacAddress srcMac = ethPkt.getSourceMAC();
Ari Saha89831742015-06-26 10:31:48 -0700450
Jonathan Harta46dddf2015-06-30 15:31:20 -0700451 DeviceId deviceId = inPacket.receivedFrom().deviceId();
452 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700453 String sessionId = deviceId.toString() + portNumber.toString();
Saurav Das987441a2018-09-18 16:33:47 -0700454 EAPOL eapol = (EAPOL) ethPkt.getPayload();
455 if (log.isTraceEnabled()) {
456 log.trace("Received EAPOL packet {} in enclosing packet {} from "
457 + "dev/port: {}/{}", eapol, ethPkt, deviceId,
458 portNumber);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100459 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700460
Saurav Das987441a2018-09-18 16:33:47 -0700461 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
462 if (stateMachine == null) {
463 log.debug("Creating new state machine for sessionId: {} for "
464 + "dev/port: {}/{}", sessionId, deviceId, portNumber);
465 stateMachine = new StateMachine(sessionId);
466 } else {
467 log.debug("Using existing state-machine for sessionId: {}", sessionId);
468 }
469
Ari Saha89831742015-06-26 10:31:48 -0700470
471 switch (eapol.getEapolType()) {
472 case EAPOL.EAPOL_START:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400473 log.debug("EAP packet: EAPOL_START");
Ray Milkeyf51eba22015-09-25 10:24:23 -0700474 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Jonathan Hart5db44532018-07-12 18:13:54 -0700475 stateMachine.start();
Ari Saha89831742015-06-26 10:31:48 -0700476
Ray Milkeyf51eba22015-09-25 10:24:23 -0700477 //send an EAP Request/Identify to the supplicant
478 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100479 if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
480 stateMachine.setPriorityCode(ethPkt.getPriorityCode());
481 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800482 Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
Ray Milkeyf51eba22015-09-25 10:24:23 -0700483 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100484 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400485
Jonathan Hart092dfb22015-11-16 23:05:21 -0800486 stateMachine.setSupplicantAddress(srcMac);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700487 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700488
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400489 log.debug("Getting EAP identity from supplicant {}", stateMachine.supplicantAddress().toString());
Ray Milkey967776a2015-10-07 14:37:17 -0700490 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700491
492 break;
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800493 case EAPOL.EAPOL_LOGOFF:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400494 log.debug("EAP packet: EAPOL_LOGOFF");
Ray Milkeyb34b4962016-01-04 10:24:43 -0800495 if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
496 stateMachine.logoff();
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800497 }
498
499 break;
Ari Saha89831742015-06-26 10:31:48 -0700500 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700501 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700502 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700503 EAP eapPacket = (EAP) eapol.getPayload();
504
505 byte dataType = eapPacket.getDataType();
506 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700507
Ray Milkey9eb293f2015-09-30 15:09:17 -0700508 case EAP.ATTR_IDENTITY:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400509 log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700510 // request id access to RADIUS
511 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700512
Ray Milkey967776a2015-10-07 14:37:17 -0700513 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100514 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800515 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Ari Saha89831742015-06-26 10:31:48 -0700516
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100517 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700518
Ray Milkey9eb293f2015-09-30 15:09:17 -0700519 // change the state to "PENDING"
520 stateMachine.requestAccess();
521 break;
Ari Saha89831742015-06-26 10:31:48 -0700522 case EAP.ATTR_MD5:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400523 log.debug("EAP packet: EAPOL_PACKET ATTR_MD5");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700524 // verify if the EAP identifier corresponds to the
525 // challenge identifier from the client state
526 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700527 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700528 //send the RADIUS challenge response
Ray Milkey967776a2015-10-07 14:37:17 -0700529 radiusPayload =
530 getRadiusPayload(stateMachine,
531 stateMachine.identifier(),
532 eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100533 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700534
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800535 if (stateMachine.challengeState() != null) {
536 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
537 stateMachine.challengeState());
538 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800539 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100540 sendRadiusPacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700541 }
542 break;
543 case EAP.ATTR_TLS:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400544 log.debug("EAP packet: EAPOL_PACKET ATTR_TLS");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700545 // request id access to RADIUS
Ray Milkey967776a2015-10-07 14:37:17 -0700546 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100547 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700548
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800549 if (stateMachine.challengeState() != null) {
550 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
551 stateMachine.challengeState());
552 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700553 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700554
Jonathan Hart092dfb22015-11-16 23:05:21 -0800555 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100556 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkey5493b512015-10-21 12:13:49 -0700557
Ray Milkeyf3790b82015-10-21 16:28:08 -0700558 if (stateMachine.state() != StateMachine.STATE_PENDING) {
559 stateMachine.requestAccess();
560 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700561
Ari Saha89831742015-06-26 10:31:48 -0700562 break;
563 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400564 log.warn("Unknown EAP packet type");
Ari Saha89831742015-06-26 10:31:48 -0700565 return;
566 }
567 break;
568 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400569 log.debug("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700570 }
571 }
Ray Milkey967776a2015-10-07 14:37:17 -0700572 }
573
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100574 /**
Jonathan Hart5db44532018-07-12 18:13:54 -0700575 * Delegate allowing the StateMachine to notify us of events.
576 */
577 private class InternalStateMachineDelegate implements StateMachineDelegate {
578
579 @Override
580 public void notify(AuthenticationEvent authenticationEvent) {
581 log.info("Auth event {} for {}",
582 authenticationEvent.type(), authenticationEvent.subject());
583 post(authenticationEvent);
584 }
585 }
586
587 /**
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100588 * Configuration Listener, handles change in configuration.
589 */
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700590 private class InternalConfigListener implements NetworkConfigListener {
591
592 /**
Ray Milkeyc9e8dcc2015-12-30 10:31:32 -0800593 * Reconfigures the AAA application according to the
594 * configuration parameters passed.
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700595 *
596 * @param cfg configuration object
597 */
Jonathan Hart092dfb22015-11-16 23:05:21 -0800598 private void reconfigureNetwork(AaaConfig cfg) {
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400599 log.info("Reconfiguring AaaConfig from config: {}", cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100600
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700601 if (cfg == null) {
Jonathan Hart092dfb22015-11-16 23:05:21 -0800602 newCfg = new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700603 } else {
604 newCfg = cfg;
605 }
606 if (newCfg.nasIp() != null) {
607 nasIpAddress = newCfg.nasIp();
608 }
609 if (newCfg.radiusIp() != null) {
610 radiusIpAddress = newCfg.radiusIp();
611 }
612 if (newCfg.radiusMac() != null) {
613 radiusMacAddress = newCfg.radiusMac();
614 }
615 if (newCfg.nasMac() != null) {
616 nasMacAddress = newCfg.nasMac();
617 }
618 if (newCfg.radiusSecret() != null) {
619 radiusSecret = newCfg.radiusSecret();
620 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100621
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530622 boolean reconfigureCustomizer = false;
623 if (customizer == null || !customizer.equals(newCfg.radiusPktCustomizer())) {
624 customizer = newCfg.radiusPktCustomizer();
625 configurePacketCustomizer();
626 reconfigureCustomizer = true;
627 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100628
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530629 if (radiusConnectionType == null
630 || reconfigureCustomizer
631 || !radiusConnectionType.equals(newCfg.radiusConnectionType())) {
632 radiusConnectionType = newCfg.radiusConnectionType();
633 if (impl != null) {
634 impl.withdrawIntercepts();
635 impl.clearLocalState();
636 }
637 configureRadiusCommunication();
638 impl.initializeLocalState(newCfg);
639 impl.requestIntercepts();
640 } else if (impl != null) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100641 impl.clearLocalState();
642 impl.initializeLocalState(newCfg);
Ray Milkey5d99bd12015-10-06 15:41:30 -0700643 }
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700644 }
645
646 @Override
647 public void event(NetworkConfigEvent event) {
648
649 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
650 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
Jonathan Hart092dfb22015-11-16 23:05:21 -0800651 event.configClass().equals(AaaConfig.class)) {
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700652
Jonathan Hart092dfb22015-11-16 23:05:21 -0800653 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700654 reconfigureNetwork(cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100655
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400656 log.info("Reconfigured: {}", cfg.toString());
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700657 }
658 }
659 }
Amit Ghoshf739be52017-09-21 15:49:37 +0100660
661 private class InternalDeviceListener implements DeviceListener {
662 @Override
663 public void event(DeviceEvent event) {
664
665 switch (event.type()) {
666 case PORT_REMOVED:
667 DeviceId devId = event.subject().id();
668 PortNumber portNumber = event.port().number();
669 String sessionId = devId.toString() + portNumber.toString();
670
671 Map<String, StateMachine> sessionIdMap = StateMachine.sessionIdMap();
672 StateMachine removed = sessionIdMap.remove(sessionId);
673 if (removed != null) {
674 StateMachine.deleteStateMachineMapping(removed);
675 }
676
677 break;
678 default:
679 return;
680 }
681 }
682 }
Ari Saha89831742015-06-26 10:31:48 -0700683}