blob: 3e75f1719d52309c9e52204143cde47e532bb3ab [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 */
Matteo Scandolocf847b82019-04-26 15:00:00 -070016package org.opencord.aaa.impl;
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;
Matteo Scandolocf847b82019-04-26 15:00:00 -070061import org.opencord.aaa.AaaConfig;
62import org.opencord.aaa.AuthenticationEvent;
63import org.opencord.aaa.AuthenticationEventListener;
64import org.opencord.aaa.AuthenticationService;
65import org.opencord.aaa.RadiusCommunicator;
66import org.opencord.aaa.StateMachineDelegate;
Gamze Abaka1cfdb192018-10-25 11:39:19 +000067import org.opencord.sadis.BaseInformationService;
68import org.opencord.sadis.SadisService;
69import org.opencord.sadis.SubscriberAndDeviceInformation;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +053070import org.osgi.service.component.annotations.Activate;
Ari Saha89831742015-06-26 10:31:48 -070071import org.slf4j.Logger;
72
Ari Saha89831742015-06-26 10:31:48 -070073/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070074 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070075 */
Jonathan Hart5db44532018-07-12 18:13:54 -070076@Service
Ari Saha89831742015-06-26 10:31:48 -070077@Component(immediate = true)
Jonathan Hart5db44532018-07-12 18:13:54 -070078public class AaaManager
79 extends AbstractListenerManager<AuthenticationEvent, AuthenticationEventListener>
80 implements AuthenticationService {
81
Charles Chandf7ff862017-01-20 11:22:05 -080082 private static final String APP_NAME = "org.opencord.aaa";
Ray Milkeyf51eba22015-09-25 10:24:23 -070083
Ray Milkeyf61a24e2015-09-24 16:34:02 -070084 private final Logger log = getLogger(getClass());
Ray Milkeyf51eba22015-09-25 10:24:23 -070085
Ray Milkeyf61a24e2015-09-24 16:34:02 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected CoreService coreService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070088
Ray Milkeyf61a24e2015-09-24 16:34:02 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected PacketService packetService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070091
Ray Milkeyf61a24e2015-09-24 16:34:02 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkeyfcb623d2015-10-01 16:48:18 -070093 protected NetworkConfigRegistry netCfgService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070094
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Gamze Abaka1cfdb192018-10-25 11:39:19 +000099 protected SadisService sadisService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected MastershipService mastershipService;
103
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000104 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
105
Amit Ghoshf739be52017-09-21 15:49:37 +0100106 private final DeviceListener deviceListener = new InternalDeviceListener();
107
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700108 // NAS IP address
109 protected InetAddress nasIpAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100110
111 // self MAC address
Jonathan Hart5db44532018-07-12 18:13:54 -0700112 protected String nasMacAddress;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100113
114 // Parsed RADIUS server addresses
115 protected InetAddress radiusIpAddress;
116
117 // MAC address of RADIUS server or net hop router
118 protected String radiusMacAddress;
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700119
120 // RADIUS server secret
121 protected String radiusSecret;
122
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100123 // bindings
124 protected CustomizationInfo customInfo;
Ray Milkey5d99bd12015-10-06 15:41:30 -0700125
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700126 // our application-specific event handler
127 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
Ray Milkeyf51eba22015-09-25 10:24:23 -0700128
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700129 // our unique identifier
130 private ApplicationId appId;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700131
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100132 // Setup specific customization/attributes on the RADIUS packets
133 PacketCustomizer pktCustomizer;
Ray Milkey967776a2015-10-07 14:37:17 -0700134
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100135 // packet customizer to use
136 private String customizer;
137
138 // Type of connection to use to communicate with Radius server, options are
139 // "socket" or "packet_out"
140 private String radiusConnectionType;
141
Jonathan Hart5db44532018-07-12 18:13:54 -0700142 // Object for the specific type of communication with the RADIUS
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100143 // server, socket based or packet_out based
144 RadiusCommunicator impl = null;
145
146 // latest configuration
147 AaaConfig newCfg;
Ray Milkey967776a2015-10-07 14:37:17 -0700148
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700149 // Configuration properties factory
150 private final ConfigFactory factory =
Jonathan Hart092dfb22015-11-16 23:05:21 -0800151 new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
152 AaaConfig.class,
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700153 "AAA") {
154 @Override
Jonathan Hart092dfb22015-11-16 23:05:21 -0800155 public AaaConfig createConfig() {
156 return new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700157 }
158 };
Ray Milkeyf51eba22015-09-25 10:24:23 -0700159
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700160 // Listener for config changes
161 private final InternalConfigListener cfgListener = new InternalConfigListener();
Ari Saha89831742015-06-26 10:31:48 -0700162
Jonathan Hart5db44532018-07-12 18:13:54 -0700163 private StateMachineDelegate delegate = new InternalStateMachineDelegate();
164
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700165 /**
166 * Builds an EAPOL packet based on the given parameters.
167 *
168 * @param dstMac destination MAC address
169 * @param srcMac source MAC address
170 * @param vlan vlan identifier
171 * @param eapolType EAPOL type
172 * @param eap EAP payload
173 * @return Ethernet frame
174 */
175 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100176 short vlan, byte eapolType, EAP eap, byte priorityCode) {
Ari Saha89831742015-06-26 10:31:48 -0700177
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700178 Ethernet eth = new Ethernet();
179 eth.setDestinationMACAddress(dstMac.toBytes());
180 eth.setSourceMACAddress(srcMac.toBytes());
181 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
182 if (vlan != Ethernet.VLAN_UNTAGGED) {
183 eth.setVlanID(vlan);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100184 eth.setPriorityCode(priorityCode);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700185 }
186 //eapol header
187 EAPOL eapol = new EAPOL();
188 eapol.setEapolType(eapolType);
189 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700190
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700191 //eap part
192 eapol.setPayload(eap);
193
194 eth.setPayload(eapol);
195 eth.setPad(true);
196 return eth;
197 }
Ari Saha89831742015-06-26 10:31:48 -0700198
Ari Saha89831742015-06-26 10:31:48 -0700199 @Activate
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700200 public void activate() {
Charles Chandf7ff862017-01-20 11:22:05 -0800201 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart5db44532018-07-12 18:13:54 -0700202 eventDispatcher.addSink(AuthenticationEvent.class, listenerRegistry);
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400203 netCfgService.addListener(cfgListener);
204 netCfgService.registerConfigFactory(factory);
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000205 subsService = sadisService.getSubscriberInfoService();
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530206 customInfo = new CustomizationInfo(subsService, deviceService);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800207 cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400208 log.info("Starting with config {} {}", this, newCfg);
209
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530210 configureRadiusCommunication();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700211
Ari Saha89831742015-06-26 10:31:48 -0700212 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700213 packetService.addProcessor(processor, PacketProcessor.director(2));
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100214
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700215
216 StateMachine.initializeMaps();
Jonathan Hart5db44532018-07-12 18:13:54 -0700217 StateMachine.setDelegate(delegate);
Ari Saha89831742015-06-26 10:31:48 -0700218
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100219 impl.initializeLocalState(newCfg);
alshabib27afc1d2016-12-23 00:33:58 -0800220
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100221 impl.requestIntercepts();
222
Amit Ghoshf739be52017-09-21 15:49:37 +0100223 deviceService.addListener(deviceListener);
224
Jian Li13c67162015-12-09 13:20:34 -0800225 log.info("Started");
Ari Saha89831742015-06-26 10:31:48 -0700226 }
227
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530228
Ari Saha89831742015-06-26 10:31:48 -0700229 @Deactivate
230 public void deactivate() {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100231 impl.withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700232 packetService.removeProcessor(processor);
Deepa Vaddireddyb9c24c62017-09-21 13:45:30 +0530233 netCfgService.removeListener(cfgListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700234 StateMachine.unsetDelegate(delegate);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530235 StateMachine.destroyMaps();
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100236 impl.deactivate();
Amit Ghoshf739be52017-09-21 15:49:37 +0100237 deviceService.removeListener(deviceListener);
Jonathan Hart5db44532018-07-12 18:13:54 -0700238 eventDispatcher.removeSink(AuthenticationEvent.class);
Jian Li13c67162015-12-09 13:20:34 -0800239 log.info("Stopped");
Ray Milkey967776a2015-10-07 14:37:17 -0700240 }
241
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530242 private void configureRadiusCommunication() {
243 if (radiusConnectionType.toLowerCase().equals("socket")) {
244 impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
245 } else {
246 impl = new PortBasedRadiusCommunicator(appId, packetService, mastershipService,
247 deviceService, subsService, pktCustomizer, this);
248 }
249 }
250
251 private void configurePacketCustomizer() {
252 switch (customizer.toLowerCase()) {
253 case "sample":
254 pktCustomizer = new SamplePacketCustomizer(customInfo);
255 log.info("Created SamplePacketCustomizer");
256 break;
Saurav Dase72358a2018-11-13 21:56:46 -0800257 case "att":
258 pktCustomizer = new AttPacketCustomizer(customInfo);
259 log.info("Created AttPacketCustomizer");
260 break;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530261 default:
262 pktCustomizer = new PacketCustomizer(customInfo);
263 log.info("Created default PacketCustomizer");
264 break;
265 }
266 }
267
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100268 /**
269 * Send RADIUS packet to the RADIUS server.
270 *
271 * @param radiusPacket RADIUS packet to be sent to server.
272 * @param inPkt Incoming EAPOL packet
273 */
274 protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
275 impl.sendRadiusPacket(radiusPacket, inPkt);
276 }
Ray Milkey967776a2015-10-07 14:37:17 -0700277
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100278 /**
279 * Handles RADIUS packets.
280 *
281 * @param radiusPacket RADIUS packet coming from the RADIUS server.
282 * @throws StateMachineException if an illegal state transition is triggered
Jonathan Hart4731dd92018-05-02 17:30:05 -0700283 * @throws DeserializationException if packet deserialization fails
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100284 */
Jonathan Hart4731dd92018-05-02 17:30:05 -0700285 public void handleRadiusPacket(RADIUS radiusPacket)
286 throws StateMachineException, DeserializationException {
Saurav Das987441a2018-09-18 16:33:47 -0700287 if (log.isTraceEnabled()) {
288 log.trace("Received RADIUS packet {}", radiusPacket);
289 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100290 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
291 if (stateMachine == null) {
Saurav Das987441a2018-09-18 16:33:47 -0700292 log.error("Invalid packet identifier {}, could not find corresponding "
293 + "state machine ... exiting", radiusPacket.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100294 return;
Ray Milkey967776a2015-10-07 14:37:17 -0700295 }
Ari Saha89831742015-06-26 10:31:48 -0700296
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100297 EAP eapPayload;
298 Ethernet eth;
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700299
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100300 switch (radiusPacket.getCode()) {
301 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400302 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100303 RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
304 byte[] challengeState = null;
305 if (radiusAttrState != null) {
306 challengeState = radiusAttrState.getValue();
307 }
308 eapPayload = radiusPacket.decapsulateMessage();
309 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
310 eth = buildEapolResponse(stateMachine.supplicantAddress(),
311 MacAddress.valueOf(nasMacAddress),
312 stateMachine.vlanId(),
313 EAPOL.EAPOL_PACKET,
314 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400315 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100316 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
317 break;
318 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400319 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100320 //send an EAPOL - Success to the supplicant.
321 byte[] eapMessageSuccess =
322 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700323 eapPayload = EAP.deserializer().deserialize(
324 eapMessageSuccess, 0, eapMessageSuccess.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100325 eth = buildEapolResponse(stateMachine.supplicantAddress(),
326 MacAddress.valueOf(nasMacAddress),
327 stateMachine.vlanId(),
328 EAPOL.EAPOL_PACKET,
329 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400330 log.info("Send EAP success message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100331 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
332
333 stateMachine.authorizeAccess();
334
335 break;
336 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400337 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100338 //send an EAPOL - Failure to the supplicant.
339 byte[] eapMessageFailure;
340 eapPayload = new EAP();
341 RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
342 if (radiusAttrEap == null) {
343 eapPayload.setCode(EAP.FAILURE);
344 eapPayload.setIdentifier(stateMachine.challengeIdentifier());
345 eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
346 } else {
347 eapMessageFailure = radiusAttrEap.getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700348 eapPayload = EAP.deserializer().deserialize(
349 eapMessageFailure, 0, eapMessageFailure.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100350 }
351 eth = buildEapolResponse(stateMachine.supplicantAddress(),
352 MacAddress.valueOf(nasMacAddress),
353 stateMachine.vlanId(),
354 EAPOL.EAPOL_PACKET,
355 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400356 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100357 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
358 stateMachine.denyAccess();
359
360 break;
361 default:
362 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
363 }
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700364 }
365
Ray Milkey967776a2015-10-07 14:37:17 -0700366 /**
367 * Send the ethernet packet to the supplicant.
368 *
369 * @param ethernetPkt the ethernet packet
370 * @param connectPoint the connect point to send out
371 */
372 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
373 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
374 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
375 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Saurav Das987441a2018-09-18 16:33:47 -0700376 if (log.isTraceEnabled()) {
377 EAPOL eap = ((EAPOL) ethernetPkt.getPayload());
378 log.trace("Sending eapol payload {} enclosed in {} to supplicant at {}",
379 eap, ethernetPkt, connectPoint);
380 }
Ray Milkey967776a2015-10-07 14:37:17 -0700381 packetService.emit(packet);
382 }
383
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400384 @Override
385 public String toString() {
386 return ToStringBuilder.reflectionToString(this);
387 }
388
Ari Saha89831742015-06-26 10:31:48 -0700389 // our handler defined as a private inner class
390
391 /**
392 * Packet processor responsible for forwarding packets along their paths.
393 */
394 private class ReactivePacketProcessor implements PacketProcessor {
395 @Override
396 public void process(PacketContext context) {
397
398 // Extract the original Ethernet frame from the packet information
399 InboundPacket pkt = context.inPacket();
400 Ethernet ethPkt = pkt.parsed();
401 if (ethPkt == null) {
402 return;
403 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100404
Ray Milkeyf51eba22015-09-25 10:24:23 -0700405 try {
406 // identify if incoming packet comes from supplicant (EAP) or RADIUS
407 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
408 case EAPOL:
409 handleSupplicantPacket(context.inPacket());
410 break;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700411 default:
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100412 // any other packets let the specific implementation handle
413 impl.handlePacketFromServer(context);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700414 }
Ray Milkey967776a2015-10-07 14:37:17 -0700415 } catch (StateMachineException e) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100416 log.warn("Unable to process packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700417 }
418 }
419
Ray Milkey9eb293f2015-09-30 15:09:17 -0700420 /**
421 * Creates and initializes common fields of a RADIUS packet.
422 *
Ray Milkey967776a2015-10-07 14:37:17 -0700423 * @param stateMachine state machine for the request
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700424 * @param eapPacket EAP packet
Ray Milkey9eb293f2015-09-30 15:09:17 -0700425 * @return RADIUS packet
426 */
Ray Milkey967776a2015-10-07 14:37:17 -0700427 private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
Ray Milkey9eb293f2015-09-30 15:09:17 -0700428 RADIUS radiusPayload =
429 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
430 eapPacket.getIdentifier());
Ray Milkey967776a2015-10-07 14:37:17 -0700431
432 // set Request Authenticator in StateMachine
433 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
434
Ray Milkey9eb293f2015-09-30 15:09:17 -0700435 radiusPayload.setIdentifier(identifier);
436 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkey967776a2015-10-07 14:37:17 -0700437 stateMachine.username());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700438
439 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Jonathan Hart092dfb22015-11-16 23:05:21 -0800440 AaaManager.this.nasIpAddress.getAddress());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700441
442 radiusPayload.encapsulateMessage(eapPacket);
Ray Milkey9eb293f2015-09-30 15:09:17 -0700443
444 return radiusPayload;
445 }
Ari Saha89831742015-06-26 10:31:48 -0700446
447 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700448 * Handles PAE packets (supplicant).
449 *
450 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700451 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700452 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700453 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700454 // Where does it come from?
Jonathan Hart092dfb22015-11-16 23:05:21 -0800455 MacAddress srcMac = ethPkt.getSourceMAC();
Ari Saha89831742015-06-26 10:31:48 -0700456
Jonathan Harta46dddf2015-06-30 15:31:20 -0700457 DeviceId deviceId = inPacket.receivedFrom().deviceId();
458 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700459 String sessionId = deviceId.toString() + portNumber.toString();
Saurav Das987441a2018-09-18 16:33:47 -0700460 EAPOL eapol = (EAPOL) ethPkt.getPayload();
461 if (log.isTraceEnabled()) {
462 log.trace("Received EAPOL packet {} in enclosing packet {} from "
463 + "dev/port: {}/{}", eapol, ethPkt, deviceId,
464 portNumber);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100465 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700466
Saurav Das987441a2018-09-18 16:33:47 -0700467 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
468 if (stateMachine == null) {
469 log.debug("Creating new state machine for sessionId: {} for "
470 + "dev/port: {}/{}", sessionId, deviceId, portNumber);
471 stateMachine = new StateMachine(sessionId);
472 } else {
473 log.debug("Using existing state-machine for sessionId: {}", sessionId);
474 }
475
Ari Saha89831742015-06-26 10:31:48 -0700476
477 switch (eapol.getEapolType()) {
478 case EAPOL.EAPOL_START:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400479 log.debug("EAP packet: EAPOL_START");
Ray Milkeyf51eba22015-09-25 10:24:23 -0700480 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Jonathan Hart5db44532018-07-12 18:13:54 -0700481 stateMachine.start();
Ari Saha89831742015-06-26 10:31:48 -0700482
Ray Milkeyf51eba22015-09-25 10:24:23 -0700483 //send an EAP Request/Identify to the supplicant
484 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100485 if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
486 stateMachine.setPriorityCode(ethPkt.getPriorityCode());
487 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800488 Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
Ray Milkeyf51eba22015-09-25 10:24:23 -0700489 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100490 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400491
Jonathan Hart092dfb22015-11-16 23:05:21 -0800492 stateMachine.setSupplicantAddress(srcMac);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700493 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700494
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400495 log.debug("Getting EAP identity from supplicant {}", stateMachine.supplicantAddress().toString());
Ray Milkey967776a2015-10-07 14:37:17 -0700496 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700497
498 break;
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800499 case EAPOL.EAPOL_LOGOFF:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400500 log.debug("EAP packet: EAPOL_LOGOFF");
Ray Milkeyb34b4962016-01-04 10:24:43 -0800501 if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
502 stateMachine.logoff();
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800503 }
504
505 break;
Ari Saha89831742015-06-26 10:31:48 -0700506 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700507 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700508 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700509 EAP eapPacket = (EAP) eapol.getPayload();
510
511 byte dataType = eapPacket.getDataType();
512 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700513
Ray Milkey9eb293f2015-09-30 15:09:17 -0700514 case EAP.ATTR_IDENTITY:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400515 log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700516 // request id access to RADIUS
517 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700518
Ray Milkey967776a2015-10-07 14:37:17 -0700519 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100520 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800521 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Ari Saha89831742015-06-26 10:31:48 -0700522
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100523 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700524
Ray Milkey9eb293f2015-09-30 15:09:17 -0700525 // change the state to "PENDING"
526 stateMachine.requestAccess();
527 break;
Ari Saha89831742015-06-26 10:31:48 -0700528 case EAP.ATTR_MD5:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400529 log.debug("EAP packet: EAPOL_PACKET ATTR_MD5");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700530 // verify if the EAP identifier corresponds to the
531 // challenge identifier from the client state
532 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700533 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700534 //send the RADIUS challenge response
Ray Milkey967776a2015-10-07 14:37:17 -0700535 radiusPayload =
536 getRadiusPayload(stateMachine,
537 stateMachine.identifier(),
538 eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100539 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700540
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800541 if (stateMachine.challengeState() != null) {
542 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
543 stateMachine.challengeState());
544 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800545 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100546 sendRadiusPacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700547 }
548 break;
549 case EAP.ATTR_TLS:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400550 log.debug("EAP packet: EAPOL_PACKET ATTR_TLS");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700551 // request id access to RADIUS
Ray Milkey967776a2015-10-07 14:37:17 -0700552 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100553 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700554
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800555 if (stateMachine.challengeState() != null) {
556 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
557 stateMachine.challengeState());
558 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700559 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700560
Jonathan Hart092dfb22015-11-16 23:05:21 -0800561 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100562 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkey5493b512015-10-21 12:13:49 -0700563
Ray Milkeyf3790b82015-10-21 16:28:08 -0700564 if (stateMachine.state() != StateMachine.STATE_PENDING) {
565 stateMachine.requestAccess();
566 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700567
Ari Saha89831742015-06-26 10:31:48 -0700568 break;
569 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400570 log.warn("Unknown EAP packet type");
Ari Saha89831742015-06-26 10:31:48 -0700571 return;
572 }
573 break;
574 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400575 log.debug("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700576 }
577 }
Ray Milkey967776a2015-10-07 14:37:17 -0700578 }
579
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100580 /**
Jonathan Hart5db44532018-07-12 18:13:54 -0700581 * Delegate allowing the StateMachine to notify us of events.
582 */
583 private class InternalStateMachineDelegate implements StateMachineDelegate {
584
585 @Override
586 public void notify(AuthenticationEvent authenticationEvent) {
587 log.info("Auth event {} for {}",
588 authenticationEvent.type(), authenticationEvent.subject());
589 post(authenticationEvent);
590 }
591 }
592
593 /**
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100594 * Configuration Listener, handles change in configuration.
595 */
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700596 private class InternalConfigListener implements NetworkConfigListener {
597
598 /**
Ray Milkeyc9e8dcc2015-12-30 10:31:32 -0800599 * Reconfigures the AAA application according to the
600 * configuration parameters passed.
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700601 *
602 * @param cfg configuration object
603 */
Jonathan Hart092dfb22015-11-16 23:05:21 -0800604 private void reconfigureNetwork(AaaConfig cfg) {
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400605 log.info("Reconfiguring AaaConfig from config: {}", cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100606
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700607 if (cfg == null) {
Jonathan Hart092dfb22015-11-16 23:05:21 -0800608 newCfg = new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700609 } else {
610 newCfg = cfg;
611 }
612 if (newCfg.nasIp() != null) {
613 nasIpAddress = newCfg.nasIp();
614 }
615 if (newCfg.radiusIp() != null) {
616 radiusIpAddress = newCfg.radiusIp();
617 }
618 if (newCfg.radiusMac() != null) {
619 radiusMacAddress = newCfg.radiusMac();
620 }
621 if (newCfg.nasMac() != null) {
622 nasMacAddress = newCfg.nasMac();
623 }
624 if (newCfg.radiusSecret() != null) {
625 radiusSecret = newCfg.radiusSecret();
626 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100627
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530628 boolean reconfigureCustomizer = false;
629 if (customizer == null || !customizer.equals(newCfg.radiusPktCustomizer())) {
630 customizer = newCfg.radiusPktCustomizer();
631 configurePacketCustomizer();
632 reconfigureCustomizer = true;
633 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100634
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530635 if (radiusConnectionType == null
636 || reconfigureCustomizer
637 || !radiusConnectionType.equals(newCfg.radiusConnectionType())) {
638 radiusConnectionType = newCfg.radiusConnectionType();
639 if (impl != null) {
640 impl.withdrawIntercepts();
641 impl.clearLocalState();
642 }
643 configureRadiusCommunication();
644 impl.initializeLocalState(newCfg);
645 impl.requestIntercepts();
646 } else if (impl != null) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100647 impl.clearLocalState();
648 impl.initializeLocalState(newCfg);
Ray Milkey5d99bd12015-10-06 15:41:30 -0700649 }
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700650 }
651
652 @Override
653 public void event(NetworkConfigEvent event) {
654
655 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
656 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
Jonathan Hart092dfb22015-11-16 23:05:21 -0800657 event.configClass().equals(AaaConfig.class)) {
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700658
Jonathan Hart092dfb22015-11-16 23:05:21 -0800659 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700660 reconfigureNetwork(cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100661
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400662 log.info("Reconfigured: {}", cfg.toString());
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700663 }
664 }
665 }
Amit Ghoshf739be52017-09-21 15:49:37 +0100666
667 private class InternalDeviceListener implements DeviceListener {
668 @Override
669 public void event(DeviceEvent event) {
670
671 switch (event.type()) {
672 case PORT_REMOVED:
673 DeviceId devId = event.subject().id();
674 PortNumber portNumber = event.port().number();
675 String sessionId = devId.toString() + portNumber.toString();
676
677 Map<String, StateMachine> sessionIdMap = StateMachine.sessionIdMap();
678 StateMachine removed = sessionIdMap.remove(sessionId);
679 if (removed != null) {
680 StateMachine.deleteStateMachineMapping(removed);
681 }
682
683 break;
684 default:
685 return;
686 }
687 }
688 }
Ari Saha89831742015-06-26 10:31:48 -0700689}