blob: c386365ced922a2329be71fdee6010d74e927af0 [file] [log] [blame]
Ari Saha89831742015-06-26 10:31:48 -07001/*
2 * Copyright 2015 AT&T Foundry
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.aaa;
17
18import com.google.common.base.Strings;
Jonathan Harta46dddf2015-06-30 15:31:20 -070019import com.google.common.collect.Maps;
Ari Saha89831742015-06-26 10:31:48 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harta46dddf2015-06-30 15:31:20 -070027import org.onlab.packet.DeserializationException;
28import org.onlab.packet.EAP;
29import org.onlab.packet.EAPOL;
30import org.onlab.packet.EthType;
Ari Saha89831742015-06-26 10:31:48 -070031import org.onlab.packet.Ethernet;
32import org.onlab.packet.IPv4;
33import org.onlab.packet.Ip4Address;
34import org.onlab.packet.IpAddress;
35import org.onlab.packet.MacAddress;
Jonathan Harta46dddf2015-06-30 15:31:20 -070036import org.onlab.packet.RADIUS;
37import org.onlab.packet.RADIUSAttribute;
Ari Saha89831742015-06-26 10:31:48 -070038import org.onlab.packet.UDP;
39import org.onlab.packet.VlanId;
40import org.onlab.util.Tools;
Ari Saha89831742015-06-26 10:31:48 -070041import org.onosproject.cfg.ComponentConfigService;
42import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
44import org.onosproject.net.ConnectPoint;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Host;
47import org.onosproject.net.PortNumber;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070050import org.onosproject.net.flow.TrafficSelector;
51import org.onosproject.net.flow.TrafficTreatment;
52import org.onosproject.net.host.HostService;
Ari Saha89831742015-06-26 10:31:48 -070053import org.onosproject.net.packet.DefaultOutboundPacket;
54import org.onosproject.net.packet.InboundPacket;
55import org.onosproject.net.packet.OutboundPacket;
56import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070057import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
Ari Saha89831742015-06-26 10:31:48 -070059import org.onosproject.xosintegration.VoltTenantService;
60import org.osgi.service.component.ComponentContext;
61import org.slf4j.Logger;
62
63import java.net.InetAddress;
64import java.net.UnknownHostException;
65import java.nio.ByteBuffer;
66import java.util.Collections;
67import java.util.Dictionary;
Ari Saha89831742015-06-26 10:31:48 -070068import java.util.Iterator;
69import java.util.Map;
70import java.util.Optional;
71import java.util.Set;
72
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070073import static org.onosproject.net.packet.PacketPriority.CONTROL;
Ari Saha89831742015-06-26 10:31:48 -070074import static org.slf4j.LoggerFactory.getLogger;
75
76
77/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070078 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070079 */
80@Component(immediate = true)
81public class AAA {
82 // a list of our dependencies :
83 // to register with ONOS as an application - described next
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
Ari Saha89831742015-06-26 10:31:48 -070087 // to receive Packet-in events that we'll respond to
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected PacketService packetService;
90
Ari Saha89831742015-06-26 10:31:48 -070091 // end host information
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected HostService hostService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected VoltTenantService voltTenantService;
97
Ari Saha89831742015-06-26 10:31:48 -070098 // for verbose output
99 private final Logger log = getLogger(getClass());
100
101 // our application-specific event handler
102 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
103
104 // our unique identifier
105 private ApplicationId appId;
106
107 // Map of state machines. Each state machine is represented by an
108 // unique identifier on the switch: dpid + port number
109 Map stateMachineMap = null;
110
111 // RADIUS server IP address
112 private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
113 // NAS IP address
114 private static final String DEFAULT_NAS_IP = "192.168.1.11";
115 // RADIUS uplink port
116 private static final int DEFAULT_RADIUS_UPLINK = 2;
117 // RADIUS server shared secret
118 private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700119 // RADIUS MAC address
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700120 private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700121 // NAS MAC address
Ari Saha89831742015-06-26 10:31:48 -0700122 private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700123 // Radius Switch Id
alshabib32039d52015-07-01 16:35:26 -0700124 private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700125 // Radius Port Number
alshabib32039d52015-07-01 16:35:26 -0700126 private static final String DEFAULT_RADIUS_PORT = "129";
Ari Saha89831742015-06-26 10:31:48 -0700127
128 @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
129 label = "RADIUS IP Address")
130 private String radiusIpAddress = DEFAULT_RADIUS_IP;
131
132 @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700133 label = "NAS IP Address")
Ari Saha89831742015-06-26 10:31:48 -0700134 private String nasIpAddress = DEFAULT_NAS_IP;
135
136 @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
137 label = "RADIUS MAC Address")
138 private String radiusMacAddress = RADIUS_MAC_ADDRESS;
139
140 @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700141 label = "NAS MAC Address")
Ari Saha89831742015-06-26 10:31:48 -0700142 private String nasMacAddress = NAS_MAC_ADDRESS;
143
144 @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
145 label = "RADIUS shared secret")
146 private String radiusSecret = DEFAULT_RADIUS_SECRET;
147
148 @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
149 label = "Radius switch")
150 private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
151
152 @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
153 label = "Radius port")
154 private String radiusPort = DEFAULT_RADIUS_PORT;
155
156 // Parsed RADIUS server IP address
157 protected InetAddress parsedRadiusIpAddress;
158
159 // Parsed NAS IP address
160 protected InetAddress parsedNasIpAddress;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected ComponentConfigService cfgService;
164
165 @Modified
166 public void modified(ComponentContext context) {
167 Dictionary<?, ?> properties = context.getProperties();
168
169 String s = Tools.get(properties, "radiusIpAddress");
170 try {
171 parsedRadiusIpAddress = InetAddress.getByName(s);
172 radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
173 } catch (UnknownHostException e) {
174 log.error("Invalid RADIUS IP address specification: {}", s);
175 }
176 try {
177 s = Tools.get(properties, "nasIpAddress");
178 parsedNasIpAddress = InetAddress.getByName(s);
179 nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
180 } catch (UnknownHostException e) {
181 log.error("Invalid NAS IP address specification: {}", s);
182 }
183
184 s = Tools.get(properties, "radiusMacAddress");
185 radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
186
187 s = Tools.get(properties, "nasMacAddress");
188 nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
189
190 s = Tools.get(properties, "radiusSecret");
191 radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
192
193 s = Tools.get(properties, "radiusSwitchId");
194 radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
195
196 s = Tools.get(properties, "radiusPortNumber");
197 radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
198 }
199
200 @Activate
201 public void activate(ComponentContext context) {
202 cfgService.registerProperties(getClass());
203 modified(context);
204 // "org.onosproject.aaa" is the FQDN of our app
205 appId = coreService.registerApplication("org.onosproject.aaa");
206 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700207 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700208 requestIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700209 // Instantiate the map of the state machines
Jonathan Harta46dddf2015-06-30 15:31:20 -0700210 stateMachineMap = Collections.synchronizedMap(Maps.newHashMap());
Ari Saha89831742015-06-26 10:31:48 -0700211
212 hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
213
214 }
215
216 @Deactivate
217 public void deactivate() {
218 cfgService.unregisterProperties(getClass(), false);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700219
220 appId = coreService.registerApplication("org.onosproject.aaa");
221 withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700222 // de-register and null our handler
223 packetService.removeProcessor(processor);
224 processor = null;
225 }
226
Jonathan Harta46dddf2015-06-30 15:31:20 -0700227 /**
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700228 * Request packet in via PacketService.
229 */
230 private void requestIntercepts() {
231 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
232 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
233 packetService.requestPackets(selector.build(),
234 CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700235
236 TrafficSelector radSelector = DefaultTrafficSelector.builder()
237 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
238 .matchIPProtocol(IPv4.PROTOCOL_UDP)
239 .matchUdpDst((short) 1812)
240 .matchUdpSrc((short) 1812)
241 .build();
242 packetService.requestPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700243 }
244
245 /**
246 * Cancel request for packet in via PacketService.
247 */
248 private void withdrawIntercepts() {
249 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
250 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
251 packetService.cancelPackets(selector.build(), CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700252
253 TrafficSelector radSelector = DefaultTrafficSelector.builder()
254 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
255 .matchIPProtocol(IPv4.PROTOCOL_UDP)
256 .matchUdpDst((short) 1812)
257 .matchUdpSrc((short) 1812)
258 .build();
259 packetService.cancelPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700260 }
261
262 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700263 * Builds an EAPOL packet based on the given parameters.
264 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700265 * @param dstMac destination MAC address
266 * @param srcMac source MAC address
267 * @param vlan vlan identifier
Jonathan Harta46dddf2015-06-30 15:31:20 -0700268 * @param eapolType EAPOL type
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700269 * @param eap EAP payload
Jonathan Harta46dddf2015-06-30 15:31:20 -0700270 * @return Ethernet frame
271 */
272 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700273 short vlan, byte eapolType, EAP eap) {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700274
275 Ethernet eth = new Ethernet();
276 eth.setDestinationMACAddress(dstMac.toBytes());
277 eth.setSourceMACAddress(srcMac.toBytes());
278 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
279 if (vlan != Ethernet.VLAN_UNTAGGED) {
280 eth.setVlanID(vlan);
281 }
282 //eapol header
283 EAPOL eapol = new EAPOL();
284 eapol.setEapolType(eapolType);
285 eapol.setPacketLength(eap.getLength());
286
287 //eap part
288 eapol.setPayload(eap);
289
290 eth.setPayload(eapol);
291 eth.setPad(true);
292 return eth;
293 }
294
Ari Saha89831742015-06-26 10:31:48 -0700295 // our handler defined as a private inner class
296
297 /**
298 * Packet processor responsible for forwarding packets along their paths.
299 */
300 private class ReactivePacketProcessor implements PacketProcessor {
301 @Override
302 public void process(PacketContext context) {
303
304 // Extract the original Ethernet frame from the packet information
305 InboundPacket pkt = context.inPacket();
306 Ethernet ethPkt = pkt.parsed();
307 if (ethPkt == null) {
308 return;
309 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700310 // identify if incoming packet comes from supplicant (EAP) or RADIUS
311 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
312 case EAPOL:
313 handleSupplicantPacket(context.inPacket());
Ari Saha89831742015-06-26 10:31:48 -0700314 break;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700315 case IPV4:
Ari Saha89831742015-06-26 10:31:48 -0700316 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
317 Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
318 Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
319 if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
320 // TODO: check for port as well when it's configurable
321 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700322
Ari Saha89831742015-06-26 10:31:48 -0700323 byte[] datagram = udpPacket.getPayload().serialize();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700324 RADIUS radiusPacket;
325 try {
326 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
327 } catch (DeserializationException e) {
328 log.warn("Unable to deserialize RADIUS packet:", e);
329 return;
330 }
Ari Saha89831742015-06-26 10:31:48 -0700331 handleRadiusPacket(radiusPacket);
332 }
333 break;
334 default:
335 return;
336 }
337 }
338
339
340 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700341 * Handles PAE packets (supplicant).
342 *
343 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700344 */
Jonathan Harta46dddf2015-06-30 15:31:20 -0700345 private void handleSupplicantPacket(InboundPacket inPacket) {
346 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700347 // Where does it come from?
348 MacAddress srcMAC = ethPkt.getSourceMAC();
349
Jonathan Harta46dddf2015-06-30 15:31:20 -0700350 DeviceId deviceId = inPacket.receivedFrom().deviceId();
351 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700352 String sessionId = deviceId.toString() + portNumber.toString();
353 StateMachine stateMachine = getStateMachine(sessionId);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700354
355 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700356
357 switch (eapol.getEapolType()) {
358 case EAPOL.EAPOL_START:
359 try {
360 stateMachine.start();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700361 stateMachine.supplicantConnectpoint = inPacket.receivedFrom();
Ari Saha89831742015-06-26 10:31:48 -0700362
363 //send an EAP Request/Identify to the supplicant
364 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700365 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
366 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
367 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700368 stateMachine.supplicantAddress = srcMAC;
369 stateMachine.vlanId = ethPkt.getVlanID();
370
371 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
372 } catch (StateMachineException e) {
373 e.printStackTrace();
374 }
375
376 break;
377 case EAPOL.EAPOL_PACKET:
Jonathan Harta46dddf2015-06-30 15:31:20 -0700378 //check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700379 EAP eapPacket = (EAP) eapol.getPayload();
380
381 byte dataType = eapPacket.getDataType();
382 switch (dataType) {
383 case EAP.ATTR_IDENTITY:
384 try {
385 //request id access to RADIUS
386 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700387 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700388 radiusPayload.setIdentifier(stateMachine.getIdentifier());
389 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700390 eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700391 stateMachine.setUsername(eapPacket.getData());
392 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700393 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700394
395 radiusPayload.encapsulateMessage(eapPacket);
396
397 // set Request Authenticator in StateMachine
398 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
399 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
400 sendRadiusMessage(radiusPayload);
401
402 //change the state to "PENDING"
403 stateMachine.requestAccess();
404 } catch (StateMachineException e) {
405 e.printStackTrace();
406 }
407 break;
408 case EAP.ATTR_MD5:
409 //verify if the EAP identifier corresponds to the challenge identifier from the client state
410 //machine.
411 if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
412 //send the RADIUS challenge response
413 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700414 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700415 radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
416 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700417 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700418 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700419 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700420
421 radiusPayload.encapsulateMessage(eapPacket);
422
423 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700424 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700425 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
426 sendRadiusMessage(radiusPayload);
427 }
428 break;
429 case EAP.ATTR_TLS:
430 try {
431 //request id access to RADIUS
432 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700433 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700434 radiusPayload.setIdentifier(stateMachine.getIdentifier());
435 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700436 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700437 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700438 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700439
440 radiusPayload.encapsulateMessage(eapPacket);
441
442 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700443 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700444 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
445
446 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
447
448 sendRadiusMessage(radiusPayload);
449 // TODO: this gets called on every fragment, should only be called at TLS-Start
450 stateMachine.requestAccess();
451 } catch (StateMachineException e) {
452 e.printStackTrace();
453 }
454 break;
455 default:
456 return;
457 }
458 break;
459 default:
460 return;
461 }
462 }
463
464 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700465 * Handles RADIUS packets.
466 *
Ari Saha89831742015-06-26 10:31:48 -0700467 * @param radiusPacket RADIUS packet coming from the RADIUS server.
468 */
469 private void handleRadiusPacket(RADIUS radiusPacket) {
470 StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
471 if (stateMachine == null) {
472 log.error("Invalid session identifier, exiting...");
473 return;
474 }
475
Ari Saha89831742015-06-26 10:31:48 -0700476 EAP eapPayload = new EAP();
477 Ethernet eth = null;
478 switch (radiusPacket.getCode()) {
479 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
480 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
481 eapPayload = radiusPacket.decapsulateMessage();
482 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700483 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700484 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
485 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700486 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
487 break;
488 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
489 try {
490 //send an EAPOL - Success to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700491 byte[] eapMessage =
Ari Saha89831742015-06-26 10:31:48 -0700492 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
493 eapPayload = new EAP();
494 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700495 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700496 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
497 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700498 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
499
500 stateMachine.authorizeAccess();
501 } catch (StateMachineException e) {
502 e.printStackTrace();
503 }
504 break;
505 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
506 try {
507 stateMachine.denyAccess();
508 } catch (StateMachineException e) {
509 e.printStackTrace();
510 }
511 break;
512 default:
513 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
514 }
515 }
516
517 private StateMachine getStateMachineById(byte identifier) {
518 StateMachine stateMachine = null;
519 Set stateMachineSet = stateMachineMap.entrySet();
520
521 synchronized (stateMachineMap) {
522 Iterator itr = stateMachineSet.iterator();
523 while (itr.hasNext()) {
524 Map.Entry entry = (Map.Entry) itr.next();
525 stateMachine = (StateMachine) entry.getValue();
526 if (identifier == stateMachine.getIdentifier()) {
527 //the state machine has already been created for this session session
528 stateMachine = (StateMachine) entry.getValue();
529 break;
530 }
531 }
532 }
533
534 return stateMachine;
535 }
536
537 private StateMachine getStateMachine(String sessionId) {
538 StateMachine stateMachine = null;
539 Set stateMachineSet = stateMachineMap.entrySet();
540
541 synchronized (stateMachineMap) {
542 Iterator itr = stateMachineSet.iterator();
543 while (itr.hasNext()) {
544
545 Map.Entry entry = (Map.Entry) itr.next();
546 if (sessionId.equals(entry.getKey())) {
547 //the state machine has already been created for this session session
548 stateMachine = (StateMachine) entry.getValue();
549 break;
550 }
551 }
552 }
553
554 if (stateMachine == null) {
555 stateMachine = new StateMachine(sessionId, voltTenantService);
556 stateMachineMap.put(sessionId, stateMachine);
557 }
558
559 return stateMachine;
560 }
561
562 private void sendRadiusMessage(RADIUS radiusMessage) {
563 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
564 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
565
566 Host dst;
567 if (!odst.isPresent()) {
568 log.info("Radius server {} is not present", radiusIpAddress);
569 return;
570 } else {
571 dst = odst.get();
572 }
573
574 UDP udp = new UDP();
575 IPv4 ip4Packet = new IPv4();
576 Ethernet ethPkt = new Ethernet();
577 radiusMessage.setParent(udp);
578 udp.setDestinationPort((short) 1812);
579 udp.setSourcePort((short) 1812); // TODO: make this configurable
580 udp.setPayload(radiusMessage);
581 udp.setParent(ip4Packet);
582 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
583 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
584 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
585 ip4Packet.setPayload(udp);
586 ip4Packet.setParent(ethPkt);
587 ethPkt.setDestinationMACAddress(radiusMacAddress);
588 ethPkt.setSourceMACAddress(nasMacAddress);
589 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
590 ethPkt.setPayload(ip4Packet);
591
592 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
593 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
594 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700595 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700596 packetService.emit(packet);
597
598 }
599
Ari Saha89831742015-06-26 10:31:48 -0700600 /**
601 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700602 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700603 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700604 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700605 */
606 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
607 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
608 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700609 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700610 packetService.emit(packet);
611 }
612
613 }
614
615}