blob: b527d915c8c55a73a9d625b23aded529b0bad937 [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;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010061import org.opencord.sadis.SubscriberAndDeviceInformationService;
Deepa Vaddireddye0e10722017-09-27 05:00:10 +053062import org.osgi.service.component.annotations.Activate;
Ari Saha89831742015-06-26 10:31:48 -070063import org.slf4j.Logger;
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 {
Saurav Das987441a2018-09-18 16:33:47 -0700272 if (log.isTraceEnabled()) {
273 log.trace("Received RADIUS packet {}", radiusPacket);
274 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100275 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
276 if (stateMachine == null) {
Saurav Das987441a2018-09-18 16:33:47 -0700277 log.error("Invalid packet identifier {}, could not find corresponding "
278 + "state machine ... exiting", radiusPacket.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100279 return;
Ray Milkey967776a2015-10-07 14:37:17 -0700280 }
Ari Saha89831742015-06-26 10:31:48 -0700281
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100282 EAP eapPayload;
283 Ethernet eth;
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700284
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100285 switch (radiusPacket.getCode()) {
286 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400287 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100288 RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
289 byte[] challengeState = null;
290 if (radiusAttrState != null) {
291 challengeState = radiusAttrState.getValue();
292 }
293 eapPayload = radiusPacket.decapsulateMessage();
294 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
295 eth = buildEapolResponse(stateMachine.supplicantAddress(),
296 MacAddress.valueOf(nasMacAddress),
297 stateMachine.vlanId(),
298 EAPOL.EAPOL_PACKET,
299 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400300 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100301 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
302 break;
303 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400304 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100305 //send an EAPOL - Success to the supplicant.
306 byte[] eapMessageSuccess =
307 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700308 eapPayload = EAP.deserializer().deserialize(
309 eapMessageSuccess, 0, eapMessageSuccess.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100310 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.info("Send EAP success message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100316 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
317
318 stateMachine.authorizeAccess();
319
320 break;
321 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400322 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100323 //send an EAPOL - Failure to the supplicant.
324 byte[] eapMessageFailure;
325 eapPayload = new EAP();
326 RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
327 if (radiusAttrEap == null) {
328 eapPayload.setCode(EAP.FAILURE);
329 eapPayload.setIdentifier(stateMachine.challengeIdentifier());
330 eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
331 } else {
332 eapMessageFailure = radiusAttrEap.getValue();
Jonathan Hart4731dd92018-05-02 17:30:05 -0700333 eapPayload = EAP.deserializer().deserialize(
334 eapMessageFailure, 0, eapMessageFailure.length);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100335 }
336 eth = buildEapolResponse(stateMachine.supplicantAddress(),
337 MacAddress.valueOf(nasMacAddress),
338 stateMachine.vlanId(),
339 EAPOL.EAPOL_PACKET,
340 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400341 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100342 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
343 stateMachine.denyAccess();
344
345 break;
346 default:
347 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
348 }
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700349 }
350
Ray Milkey967776a2015-10-07 14:37:17 -0700351 /**
352 * Send the ethernet packet to the supplicant.
353 *
354 * @param ethernetPkt the ethernet packet
355 * @param connectPoint the connect point to send out
356 */
357 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
358 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
359 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
360 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Saurav Das987441a2018-09-18 16:33:47 -0700361 if (log.isTraceEnabled()) {
362 EAPOL eap = ((EAPOL) ethernetPkt.getPayload());
363 log.trace("Sending eapol payload {} enclosed in {} to supplicant at {}",
364 eap, ethernetPkt, connectPoint);
365 }
Ray Milkey967776a2015-10-07 14:37:17 -0700366 packetService.emit(packet);
367 }
368
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400369 @Override
370 public String toString() {
371 return ToStringBuilder.reflectionToString(this);
372 }
373
Ari Saha89831742015-06-26 10:31:48 -0700374 // our handler defined as a private inner class
375
376 /**
377 * Packet processor responsible for forwarding packets along their paths.
378 */
379 private class ReactivePacketProcessor implements PacketProcessor {
380 @Override
381 public void process(PacketContext context) {
382
383 // Extract the original Ethernet frame from the packet information
384 InboundPacket pkt = context.inPacket();
385 Ethernet ethPkt = pkt.parsed();
386 if (ethPkt == null) {
387 return;
388 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100389
Ray Milkeyf51eba22015-09-25 10:24:23 -0700390 try {
391 // identify if incoming packet comes from supplicant (EAP) or RADIUS
392 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
393 case EAPOL:
394 handleSupplicantPacket(context.inPacket());
395 break;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700396 default:
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100397 // any other packets let the specific implementation handle
398 impl.handlePacketFromServer(context);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700399 }
Ray Milkey967776a2015-10-07 14:37:17 -0700400 } catch (StateMachineException e) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100401 log.warn("Unable to process packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700402 }
403 }
404
Ray Milkey9eb293f2015-09-30 15:09:17 -0700405 /**
406 * Creates and initializes common fields of a RADIUS packet.
407 *
Ray Milkey967776a2015-10-07 14:37:17 -0700408 * @param stateMachine state machine for the request
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700409 * @param eapPacket EAP packet
Ray Milkey9eb293f2015-09-30 15:09:17 -0700410 * @return RADIUS packet
411 */
Ray Milkey967776a2015-10-07 14:37:17 -0700412 private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
Ray Milkey9eb293f2015-09-30 15:09:17 -0700413 RADIUS radiusPayload =
414 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
415 eapPacket.getIdentifier());
Ray Milkey967776a2015-10-07 14:37:17 -0700416
417 // set Request Authenticator in StateMachine
418 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
419
Ray Milkey9eb293f2015-09-30 15:09:17 -0700420 radiusPayload.setIdentifier(identifier);
421 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkey967776a2015-10-07 14:37:17 -0700422 stateMachine.username());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700423
424 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Jonathan Hart092dfb22015-11-16 23:05:21 -0800425 AaaManager.this.nasIpAddress.getAddress());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700426
427 radiusPayload.encapsulateMessage(eapPacket);
Ray Milkey9eb293f2015-09-30 15:09:17 -0700428
429 return radiusPayload;
430 }
Ari Saha89831742015-06-26 10:31:48 -0700431
432 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700433 * Handles PAE packets (supplicant).
434 *
435 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700436 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700437 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700438 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700439 // Where does it come from?
Jonathan Hart092dfb22015-11-16 23:05:21 -0800440 MacAddress srcMac = ethPkt.getSourceMAC();
Ari Saha89831742015-06-26 10:31:48 -0700441
Jonathan Harta46dddf2015-06-30 15:31:20 -0700442 DeviceId deviceId = inPacket.receivedFrom().deviceId();
443 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700444 String sessionId = deviceId.toString() + portNumber.toString();
Saurav Das987441a2018-09-18 16:33:47 -0700445 EAPOL eapol = (EAPOL) ethPkt.getPayload();
446 if (log.isTraceEnabled()) {
447 log.trace("Received EAPOL packet {} in enclosing packet {} from "
448 + "dev/port: {}/{}", eapol, ethPkt, deviceId,
449 portNumber);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100450 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700451
Saurav Das987441a2018-09-18 16:33:47 -0700452 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
453 if (stateMachine == null) {
454 log.debug("Creating new state machine for sessionId: {} for "
455 + "dev/port: {}/{}", sessionId, deviceId, portNumber);
456 stateMachine = new StateMachine(sessionId);
457 } else {
458 log.debug("Using existing state-machine for sessionId: {}", sessionId);
459 }
460
Ari Saha89831742015-06-26 10:31:48 -0700461
462 switch (eapol.getEapolType()) {
463 case EAPOL.EAPOL_START:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400464 log.debug("EAP packet: EAPOL_START");
Ray Milkeyf51eba22015-09-25 10:24:23 -0700465 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Jonathan Hart5db44532018-07-12 18:13:54 -0700466 stateMachine.start();
Ari Saha89831742015-06-26 10:31:48 -0700467
Ray Milkeyf51eba22015-09-25 10:24:23 -0700468 //send an EAP Request/Identify to the supplicant
469 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100470 if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
471 stateMachine.setPriorityCode(ethPkt.getPriorityCode());
472 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800473 Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
Ray Milkeyf51eba22015-09-25 10:24:23 -0700474 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100475 eapPayload, stateMachine.priorityCode());
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400476
Jonathan Hart092dfb22015-11-16 23:05:21 -0800477 stateMachine.setSupplicantAddress(srcMac);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700478 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700479
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400480 log.debug("Getting EAP identity from supplicant {}", stateMachine.supplicantAddress().toString());
Ray Milkey967776a2015-10-07 14:37:17 -0700481 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700482
483 break;
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800484 case EAPOL.EAPOL_LOGOFF:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400485 log.debug("EAP packet: EAPOL_LOGOFF");
Ray Milkeyb34b4962016-01-04 10:24:43 -0800486 if (stateMachine.state() == StateMachine.STATE_AUTHORIZED) {
487 stateMachine.logoff();
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800488 }
489
490 break;
Ari Saha89831742015-06-26 10:31:48 -0700491 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700492 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700493 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700494 EAP eapPacket = (EAP) eapol.getPayload();
495
496 byte dataType = eapPacket.getDataType();
497 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700498
Ray Milkey9eb293f2015-09-30 15:09:17 -0700499 case EAP.ATTR_IDENTITY:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400500 log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700501 // request id access to RADIUS
502 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700503
Ray Milkey967776a2015-10-07 14:37:17 -0700504 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100505 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Jonathan Hart092dfb22015-11-16 23:05:21 -0800506 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Ari Saha89831742015-06-26 10:31:48 -0700507
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100508 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700509
Ray Milkey9eb293f2015-09-30 15:09:17 -0700510 // change the state to "PENDING"
511 stateMachine.requestAccess();
512 break;
Ari Saha89831742015-06-26 10:31:48 -0700513 case EAP.ATTR_MD5:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400514 log.debug("EAP packet: EAPOL_PACKET ATTR_MD5");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700515 // verify if the EAP identifier corresponds to the
516 // challenge identifier from the client state
517 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700518 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700519 //send the RADIUS challenge response
Ray Milkey967776a2015-10-07 14:37:17 -0700520 radiusPayload =
521 getRadiusPayload(stateMachine,
522 stateMachine.identifier(),
523 eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100524 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700525
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800526 if (stateMachine.challengeState() != null) {
527 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
528 stateMachine.challengeState());
529 }
Jonathan Hart092dfb22015-11-16 23:05:21 -0800530 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100531 sendRadiusPacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700532 }
533 break;
534 case EAP.ATTR_TLS:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400535 log.debug("EAP packet: EAPOL_PACKET ATTR_TLS");
Ray Milkey9eb293f2015-09-30 15:09:17 -0700536 // request id access to RADIUS
Ray Milkey967776a2015-10-07 14:37:17 -0700537 radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100538 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
Ari Saha89831742015-06-26 10:31:48 -0700539
Qianqian Hub55a1ac2015-12-23 20:44:48 +0800540 if (stateMachine.challengeState() != null) {
541 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
542 stateMachine.challengeState());
543 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700544 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700545
Jonathan Hart092dfb22015-11-16 23:05:21 -0800546 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100547 sendRadiusPacket(radiusPayload, inPacket);
Ray Milkey5493b512015-10-21 12:13:49 -0700548
Ray Milkeyf3790b82015-10-21 16:28:08 -0700549 if (stateMachine.state() != StateMachine.STATE_PENDING) {
550 stateMachine.requestAccess();
551 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700552
Ari Saha89831742015-06-26 10:31:48 -0700553 break;
554 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400555 log.warn("Unknown EAP packet type");
Ari Saha89831742015-06-26 10:31:48 -0700556 return;
557 }
558 break;
559 default:
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400560 log.debug("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700561 }
562 }
Ray Milkey967776a2015-10-07 14:37:17 -0700563 }
564
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100565 /**
Jonathan Hart5db44532018-07-12 18:13:54 -0700566 * Delegate allowing the StateMachine to notify us of events.
567 */
568 private class InternalStateMachineDelegate implements StateMachineDelegate {
569
570 @Override
571 public void notify(AuthenticationEvent authenticationEvent) {
572 log.info("Auth event {} for {}",
573 authenticationEvent.type(), authenticationEvent.subject());
574 post(authenticationEvent);
575 }
576 }
577
578 /**
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100579 * Configuration Listener, handles change in configuration.
580 */
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700581 private class InternalConfigListener implements NetworkConfigListener {
582
583 /**
Ray Milkeyc9e8dcc2015-12-30 10:31:32 -0800584 * Reconfigures the AAA application according to the
585 * configuration parameters passed.
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700586 *
587 * @param cfg configuration object
588 */
Jonathan Hart092dfb22015-11-16 23:05:21 -0800589 private void reconfigureNetwork(AaaConfig cfg) {
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400590 log.info("Reconfiguring AaaConfig from config: {}", cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100591
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700592 if (cfg == null) {
Jonathan Hart092dfb22015-11-16 23:05:21 -0800593 newCfg = new AaaConfig();
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700594 } else {
595 newCfg = cfg;
596 }
597 if (newCfg.nasIp() != null) {
598 nasIpAddress = newCfg.nasIp();
599 }
600 if (newCfg.radiusIp() != null) {
601 radiusIpAddress = newCfg.radiusIp();
602 }
603 if (newCfg.radiusMac() != null) {
604 radiusMacAddress = newCfg.radiusMac();
605 }
606 if (newCfg.nasMac() != null) {
607 nasMacAddress = newCfg.nasMac();
608 }
609 if (newCfg.radiusSecret() != null) {
610 radiusSecret = newCfg.radiusSecret();
611 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100612
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530613 boolean reconfigureCustomizer = false;
614 if (customizer == null || !customizer.equals(newCfg.radiusPktCustomizer())) {
615 customizer = newCfg.radiusPktCustomizer();
616 configurePacketCustomizer();
617 reconfigureCustomizer = true;
618 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100619
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530620 if (radiusConnectionType == null
621 || reconfigureCustomizer
622 || !radiusConnectionType.equals(newCfg.radiusConnectionType())) {
623 radiusConnectionType = newCfg.radiusConnectionType();
624 if (impl != null) {
625 impl.withdrawIntercepts();
626 impl.clearLocalState();
627 }
628 configureRadiusCommunication();
629 impl.initializeLocalState(newCfg);
630 impl.requestIntercepts();
631 } else if (impl != null) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100632 impl.clearLocalState();
633 impl.initializeLocalState(newCfg);
Ray Milkey5d99bd12015-10-06 15:41:30 -0700634 }
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700635 }
636
637 @Override
638 public void event(NetworkConfigEvent event) {
639
640 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
641 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
Jonathan Hart092dfb22015-11-16 23:05:21 -0800642 event.configClass().equals(AaaConfig.class)) {
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700643
Jonathan Hart092dfb22015-11-16 23:05:21 -0800644 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700645 reconfigureNetwork(cfg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100646
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400647 log.info("Reconfigured: {}", cfg.toString());
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700648 }
649 }
650 }
Amit Ghoshf739be52017-09-21 15:49:37 +0100651
652 private class InternalDeviceListener implements DeviceListener {
653 @Override
654 public void event(DeviceEvent event) {
655
656 switch (event.type()) {
657 case PORT_REMOVED:
658 DeviceId devId = event.subject().id();
659 PortNumber portNumber = event.port().number();
660 String sessionId = devId.toString() + portNumber.toString();
661
662 Map<String, StateMachine> sessionIdMap = StateMachine.sessionIdMap();
663 StateMachine removed = sessionIdMap.remove(sessionId);
664 if (removed != null) {
665 StateMachine.deleteStateMachineMapping(removed);
666 }
667
668 break;
669 default:
670 return;
671 }
672 }
673 }
Ari Saha89831742015-06-26 10:31:48 -0700674}