blob: 7e3de8853c8eadd1b588c348984dbca14c9ec2a0 [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;
Hyunsun Moona0d63712015-08-22 21:04:23 -070038import org.onlab.packet.TpPort;
Ari Saha89831742015-06-26 10:31:48 -070039import org.onlab.packet.UDP;
40import org.onlab.packet.VlanId;
41import org.onlab.util.Tools;
Ari Saha89831742015-06-26 10:31:48 -070042import org.onosproject.cfg.ComponentConfigService;
43import org.onosproject.core.ApplicationId;
44import org.onosproject.core.CoreService;
45import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.DeviceId;
47import org.onosproject.net.Host;
48import org.onosproject.net.PortNumber;
49import org.onosproject.net.flow.DefaultTrafficSelector;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070051import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.flow.TrafficTreatment;
53import org.onosproject.net.host.HostService;
Ari Saha89831742015-06-26 10:31:48 -070054import org.onosproject.net.packet.DefaultOutboundPacket;
55import org.onosproject.net.packet.InboundPacket;
56import org.onosproject.net.packet.OutboundPacket;
57import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070058import org.onosproject.net.packet.PacketProcessor;
59import org.onosproject.net.packet.PacketService;
Ari Saha89831742015-06-26 10:31:48 -070060import org.onosproject.xosintegration.VoltTenantService;
61import org.osgi.service.component.ComponentContext;
62import org.slf4j.Logger;
63
64import java.net.InetAddress;
65import java.net.UnknownHostException;
66import java.nio.ByteBuffer;
67import java.util.Collections;
68import java.util.Dictionary;
Ari Saha89831742015-06-26 10:31:48 -070069import java.util.Iterator;
70import java.util.Map;
71import java.util.Optional;
72import java.util.Set;
73
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070074import static org.onosproject.net.packet.PacketPriority.CONTROL;
Ari Saha89831742015-06-26 10:31:48 -070075import static org.slf4j.LoggerFactory.getLogger;
76
77
78/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070079 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070080 */
81@Component(immediate = true)
82public class AAA {
83 // a list of our dependencies :
84 // to register with ONOS as an application - described next
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected CoreService coreService;
87
Ari Saha89831742015-06-26 10:31:48 -070088 // to receive Packet-in events that we'll respond to
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected PacketService packetService;
91
Ari Saha89831742015-06-26 10:31:48 -070092 // end host information
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected HostService hostService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected VoltTenantService voltTenantService;
98
Ari Saha89831742015-06-26 10:31:48 -070099 // for verbose output
100 private final Logger log = getLogger(getClass());
101
102 // our application-specific event handler
103 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
104
105 // our unique identifier
106 private ApplicationId appId;
107
108 // Map of state machines. Each state machine is represented by an
109 // unique identifier on the switch: dpid + port number
110 Map stateMachineMap = null;
111
112 // RADIUS server IP address
113 private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
114 // NAS IP address
115 private static final String DEFAULT_NAS_IP = "192.168.1.11";
116 // RADIUS uplink port
117 private static final int DEFAULT_RADIUS_UPLINK = 2;
118 // RADIUS server shared secret
119 private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700120 // RADIUS MAC address
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700121 private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700122 // NAS MAC address
Ari Saha89831742015-06-26 10:31:48 -0700123 private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700124 // Radius Switch Id
alshabib32039d52015-07-01 16:35:26 -0700125 private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700126 // Radius Port Number
alshabib32039d52015-07-01 16:35:26 -0700127 private static final String DEFAULT_RADIUS_PORT = "129";
Ari Saha89831742015-06-26 10:31:48 -0700128
129 @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
130 label = "RADIUS IP Address")
131 private String radiusIpAddress = DEFAULT_RADIUS_IP;
132
133 @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700134 label = "NAS IP Address")
Ari Saha89831742015-06-26 10:31:48 -0700135 private String nasIpAddress = DEFAULT_NAS_IP;
136
137 @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
138 label = "RADIUS MAC Address")
139 private String radiusMacAddress = RADIUS_MAC_ADDRESS;
140
141 @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700142 label = "NAS MAC Address")
Ari Saha89831742015-06-26 10:31:48 -0700143 private String nasMacAddress = NAS_MAC_ADDRESS;
144
145 @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
146 label = "RADIUS shared secret")
147 private String radiusSecret = DEFAULT_RADIUS_SECRET;
148
149 @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
150 label = "Radius switch")
151 private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
152
153 @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
154 label = "Radius port")
155 private String radiusPort = DEFAULT_RADIUS_PORT;
156
157 // Parsed RADIUS server IP address
158 protected InetAddress parsedRadiusIpAddress;
159
160 // Parsed NAS IP address
161 protected InetAddress parsedNasIpAddress;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected ComponentConfigService cfgService;
165
166 @Modified
167 public void modified(ComponentContext context) {
168 Dictionary<?, ?> properties = context.getProperties();
169
170 String s = Tools.get(properties, "radiusIpAddress");
171 try {
172 parsedRadiusIpAddress = InetAddress.getByName(s);
173 radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
174 } catch (UnknownHostException e) {
175 log.error("Invalid RADIUS IP address specification: {}", s);
176 }
177 try {
178 s = Tools.get(properties, "nasIpAddress");
179 parsedNasIpAddress = InetAddress.getByName(s);
180 nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
181 } catch (UnknownHostException e) {
182 log.error("Invalid NAS IP address specification: {}", s);
183 }
184
185 s = Tools.get(properties, "radiusMacAddress");
186 radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
187
188 s = Tools.get(properties, "nasMacAddress");
189 nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
190
191 s = Tools.get(properties, "radiusSecret");
192 radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
193
194 s = Tools.get(properties, "radiusSwitchId");
195 radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
196
197 s = Tools.get(properties, "radiusPortNumber");
198 radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
199 }
200
201 @Activate
202 public void activate(ComponentContext context) {
203 cfgService.registerProperties(getClass());
204 modified(context);
205 // "org.onosproject.aaa" is the FQDN of our app
206 appId = coreService.registerApplication("org.onosproject.aaa");
207 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700208 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700209 requestIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700210 // Instantiate the map of the state machines
Jonathan Harta46dddf2015-06-30 15:31:20 -0700211 stateMachineMap = Collections.synchronizedMap(Maps.newHashMap());
Ari Saha89831742015-06-26 10:31:48 -0700212
213 hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
214
215 }
216
217 @Deactivate
218 public void deactivate() {
219 cfgService.unregisterProperties(getClass(), false);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700220
221 appId = coreService.registerApplication("org.onosproject.aaa");
222 withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700223 // de-register and null our handler
224 packetService.removeProcessor(processor);
225 processor = null;
226 }
227
Jonathan Harta46dddf2015-06-30 15:31:20 -0700228 /**
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700229 * Request packet in via PacketService.
230 */
231 private void requestIntercepts() {
232 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
233 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
234 packetService.requestPackets(selector.build(),
235 CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700236
237 TrafficSelector radSelector = DefaultTrafficSelector.builder()
238 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
239 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700240 .matchUdpDst(TpPort.tpPort(1812))
241 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700242 .build();
243 packetService.requestPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700244 }
245
246 /**
247 * Cancel request for packet in via PacketService.
248 */
249 private void withdrawIntercepts() {
250 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
251 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
252 packetService.cancelPackets(selector.build(), CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700253
254 TrafficSelector radSelector = DefaultTrafficSelector.builder()
255 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
256 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700257 .matchUdpDst(TpPort.tpPort(1812))
258 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700259 .build();
260 packetService.cancelPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700261 }
262
263 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700264 * Builds an EAPOL packet based on the given parameters.
265 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700266 * @param dstMac destination MAC address
267 * @param srcMac source MAC address
268 * @param vlan vlan identifier
Jonathan Harta46dddf2015-06-30 15:31:20 -0700269 * @param eapolType EAPOL type
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700270 * @param eap EAP payload
Jonathan Harta46dddf2015-06-30 15:31:20 -0700271 * @return Ethernet frame
272 */
273 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700274 short vlan, byte eapolType, EAP eap) {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700275
276 Ethernet eth = new Ethernet();
277 eth.setDestinationMACAddress(dstMac.toBytes());
278 eth.setSourceMACAddress(srcMac.toBytes());
279 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
280 if (vlan != Ethernet.VLAN_UNTAGGED) {
281 eth.setVlanID(vlan);
282 }
283 //eapol header
284 EAPOL eapol = new EAPOL();
285 eapol.setEapolType(eapolType);
286 eapol.setPacketLength(eap.getLength());
287
288 //eap part
289 eapol.setPayload(eap);
290
291 eth.setPayload(eapol);
292 eth.setPad(true);
293 return eth;
294 }
295
Ari Saha89831742015-06-26 10:31:48 -0700296 // our handler defined as a private inner class
297
298 /**
299 * Packet processor responsible for forwarding packets along their paths.
300 */
301 private class ReactivePacketProcessor implements PacketProcessor {
302 @Override
303 public void process(PacketContext context) {
304
305 // Extract the original Ethernet frame from the packet information
306 InboundPacket pkt = context.inPacket();
307 Ethernet ethPkt = pkt.parsed();
308 if (ethPkt == null) {
309 return;
310 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700311 // identify if incoming packet comes from supplicant (EAP) or RADIUS
312 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
313 case EAPOL:
314 handleSupplicantPacket(context.inPacket());
Ari Saha89831742015-06-26 10:31:48 -0700315 break;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700316 case IPV4:
Ari Saha89831742015-06-26 10:31:48 -0700317 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
318 Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
319 Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
320 if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
321 // TODO: check for port as well when it's configurable
322 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700323
Ari Saha89831742015-06-26 10:31:48 -0700324 byte[] datagram = udpPacket.getPayload().serialize();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700325 RADIUS radiusPacket;
326 try {
327 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
328 } catch (DeserializationException e) {
329 log.warn("Unable to deserialize RADIUS packet:", e);
330 return;
331 }
Ari Saha89831742015-06-26 10:31:48 -0700332 handleRadiusPacket(radiusPacket);
333 }
334 break;
335 default:
336 return;
337 }
338 }
339
340
341 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700342 * Handles PAE packets (supplicant).
343 *
344 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700345 */
Jonathan Harta46dddf2015-06-30 15:31:20 -0700346 private void handleSupplicantPacket(InboundPacket inPacket) {
347 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700348 // Where does it come from?
349 MacAddress srcMAC = ethPkt.getSourceMAC();
350
Jonathan Harta46dddf2015-06-30 15:31:20 -0700351 DeviceId deviceId = inPacket.receivedFrom().deviceId();
352 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700353 String sessionId = deviceId.toString() + portNumber.toString();
354 StateMachine stateMachine = getStateMachine(sessionId);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700355
356 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700357
358 switch (eapol.getEapolType()) {
359 case EAPOL.EAPOL_START:
360 try {
361 stateMachine.start();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700362 stateMachine.supplicantConnectpoint = inPacket.receivedFrom();
Ari Saha89831742015-06-26 10:31:48 -0700363
364 //send an EAP Request/Identify to the supplicant
365 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700366 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
367 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
368 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700369 stateMachine.supplicantAddress = srcMAC;
370 stateMachine.vlanId = ethPkt.getVlanID();
371
372 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
373 } catch (StateMachineException e) {
374 e.printStackTrace();
375 }
376
377 break;
378 case EAPOL.EAPOL_PACKET:
Jonathan Harta46dddf2015-06-30 15:31:20 -0700379 //check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700380 EAP eapPacket = (EAP) eapol.getPayload();
381
382 byte dataType = eapPacket.getDataType();
383 switch (dataType) {
384 case EAP.ATTR_IDENTITY:
385 try {
386 //request id access to RADIUS
387 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700388 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700389 radiusPayload.setIdentifier(stateMachine.getIdentifier());
390 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700391 eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700392 stateMachine.setUsername(eapPacket.getData());
393 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700394 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700395
396 radiusPayload.encapsulateMessage(eapPacket);
397
398 // set Request Authenticator in StateMachine
399 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
400 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
401 sendRadiusMessage(radiusPayload);
402
403 //change the state to "PENDING"
404 stateMachine.requestAccess();
405 } catch (StateMachineException e) {
406 e.printStackTrace();
407 }
408 break;
409 case EAP.ATTR_MD5:
410 //verify if the EAP identifier corresponds to the challenge identifier from the client state
411 //machine.
412 if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
413 //send the RADIUS challenge response
414 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700415 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700416 radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
417 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700418 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700419 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700420 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700421
422 radiusPayload.encapsulateMessage(eapPacket);
423
424 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700425 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700426 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
427 sendRadiusMessage(radiusPayload);
428 }
429 break;
430 case EAP.ATTR_TLS:
431 try {
432 //request id access to RADIUS
433 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700434 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700435 radiusPayload.setIdentifier(stateMachine.getIdentifier());
436 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700437 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700438 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700439 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700440
441 radiusPayload.encapsulateMessage(eapPacket);
442
443 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700444 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700445 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
446
447 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
448
449 sendRadiusMessage(radiusPayload);
450 // TODO: this gets called on every fragment, should only be called at TLS-Start
451 stateMachine.requestAccess();
452 } catch (StateMachineException e) {
453 e.printStackTrace();
454 }
455 break;
456 default:
457 return;
458 }
459 break;
460 default:
461 return;
462 }
463 }
464
465 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700466 * Handles RADIUS packets.
467 *
Ari Saha89831742015-06-26 10:31:48 -0700468 * @param radiusPacket RADIUS packet coming from the RADIUS server.
469 */
470 private void handleRadiusPacket(RADIUS radiusPacket) {
471 StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
472 if (stateMachine == null) {
473 log.error("Invalid session identifier, exiting...");
474 return;
475 }
476
Ari Saha89831742015-06-26 10:31:48 -0700477 EAP eapPayload = new EAP();
478 Ethernet eth = null;
479 switch (radiusPacket.getCode()) {
480 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
481 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
482 eapPayload = radiusPacket.decapsulateMessage();
483 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700484 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700485 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
486 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700487 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
488 break;
489 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
490 try {
491 //send an EAPOL - Success to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700492 byte[] eapMessage =
Ari Saha89831742015-06-26 10:31:48 -0700493 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
494 eapPayload = new EAP();
495 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700496 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700497 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
498 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700499 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
500
501 stateMachine.authorizeAccess();
502 } catch (StateMachineException e) {
503 e.printStackTrace();
504 }
505 break;
506 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
507 try {
508 stateMachine.denyAccess();
509 } catch (StateMachineException e) {
510 e.printStackTrace();
511 }
512 break;
513 default:
514 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
515 }
516 }
517
518 private StateMachine getStateMachineById(byte identifier) {
519 StateMachine stateMachine = null;
520 Set stateMachineSet = stateMachineMap.entrySet();
521
522 synchronized (stateMachineMap) {
523 Iterator itr = stateMachineSet.iterator();
524 while (itr.hasNext()) {
525 Map.Entry entry = (Map.Entry) itr.next();
526 stateMachine = (StateMachine) entry.getValue();
527 if (identifier == stateMachine.getIdentifier()) {
528 //the state machine has already been created for this session session
529 stateMachine = (StateMachine) entry.getValue();
530 break;
531 }
532 }
533 }
534
535 return stateMachine;
536 }
537
538 private StateMachine getStateMachine(String sessionId) {
539 StateMachine stateMachine = null;
540 Set stateMachineSet = stateMachineMap.entrySet();
541
542 synchronized (stateMachineMap) {
543 Iterator itr = stateMachineSet.iterator();
544 while (itr.hasNext()) {
545
546 Map.Entry entry = (Map.Entry) itr.next();
547 if (sessionId.equals(entry.getKey())) {
548 //the state machine has already been created for this session session
549 stateMachine = (StateMachine) entry.getValue();
550 break;
551 }
552 }
553 }
554
555 if (stateMachine == null) {
556 stateMachine = new StateMachine(sessionId, voltTenantService);
557 stateMachineMap.put(sessionId, stateMachine);
558 }
559
560 return stateMachine;
561 }
562
563 private void sendRadiusMessage(RADIUS radiusMessage) {
564 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
565 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
566
567 Host dst;
568 if (!odst.isPresent()) {
569 log.info("Radius server {} is not present", radiusIpAddress);
570 return;
571 } else {
572 dst = odst.get();
573 }
574
575 UDP udp = new UDP();
576 IPv4 ip4Packet = new IPv4();
577 Ethernet ethPkt = new Ethernet();
578 radiusMessage.setParent(udp);
579 udp.setDestinationPort((short) 1812);
580 udp.setSourcePort((short) 1812); // TODO: make this configurable
581 udp.setPayload(radiusMessage);
582 udp.setParent(ip4Packet);
583 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
584 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
585 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
586 ip4Packet.setPayload(udp);
587 ip4Packet.setParent(ethPkt);
588 ethPkt.setDestinationMACAddress(radiusMacAddress);
589 ethPkt.setSourceMACAddress(nasMacAddress);
590 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
591 ethPkt.setPayload(ip4Packet);
592
593 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
594 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
595 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700596 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700597 packetService.emit(packet);
598
599 }
600
Ari Saha89831742015-06-26 10:31:48 -0700601 /**
602 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700603 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700604 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700605 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700606 */
607 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
608 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
609 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700610 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700611 packetService.emit(packet);
612 }
613
614 }
615
616}