blob: db4797baf711791c55a5e0da14cd68eb56168d80 [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
Ari Saha89831742015-06-26 10:31:48 -0700124 private static final String DEFAULT_RADIUS_SWITCH = "of:5e3e486e73000187";
Jonathan Harta46dddf2015-06-30 15:31:20 -0700125 // Radius Port Number
Ari Saha89831742015-06-26 10:31:48 -0700126 private static final String DEFAULT_RADIUS_PORT = "5";
127
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
207 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 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);
235 }
236
237 /**
238 * Cancel request for packet in via PacketService.
239 */
240 private void withdrawIntercepts() {
241 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
242 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
243 packetService.cancelPackets(selector.build(), CONTROL, appId);
244 }
245
246 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700247 * Builds an EAPOL packet based on the given parameters.
248 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700249 * @param dstMac destination MAC address
250 * @param srcMac source MAC address
251 * @param vlan vlan identifier
Jonathan Harta46dddf2015-06-30 15:31:20 -0700252 * @param eapolType EAPOL type
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700253 * @param eap EAP payload
Jonathan Harta46dddf2015-06-30 15:31:20 -0700254 * @return Ethernet frame
255 */
256 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700257 short vlan, byte eapolType, EAP eap) {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700258
259 Ethernet eth = new Ethernet();
260 eth.setDestinationMACAddress(dstMac.toBytes());
261 eth.setSourceMACAddress(srcMac.toBytes());
262 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
263 if (vlan != Ethernet.VLAN_UNTAGGED) {
264 eth.setVlanID(vlan);
265 }
266 //eapol header
267 EAPOL eapol = new EAPOL();
268 eapol.setEapolType(eapolType);
269 eapol.setPacketLength(eap.getLength());
270
271 //eap part
272 eapol.setPayload(eap);
273
274 eth.setPayload(eapol);
275 eth.setPad(true);
276 return eth;
277 }
278
Ari Saha89831742015-06-26 10:31:48 -0700279 // our handler defined as a private inner class
280
281 /**
282 * Packet processor responsible for forwarding packets along their paths.
283 */
284 private class ReactivePacketProcessor implements PacketProcessor {
285 @Override
286 public void process(PacketContext context) {
287
288 // Extract the original Ethernet frame from the packet information
289 InboundPacket pkt = context.inPacket();
290 Ethernet ethPkt = pkt.parsed();
291 if (ethPkt == null) {
292 return;
293 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700294 // identify if incoming packet comes from supplicant (EAP) or RADIUS
295 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
296 case EAPOL:
297 handleSupplicantPacket(context.inPacket());
Ari Saha89831742015-06-26 10:31:48 -0700298 break;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700299 case IPV4:
Ari Saha89831742015-06-26 10:31:48 -0700300 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
301 Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
302 Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
303 if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
304 // TODO: check for port as well when it's configurable
305 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700306
Ari Saha89831742015-06-26 10:31:48 -0700307 byte[] datagram = udpPacket.getPayload().serialize();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700308 RADIUS radiusPacket;
309 try {
310 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
311 } catch (DeserializationException e) {
312 log.warn("Unable to deserialize RADIUS packet:", e);
313 return;
314 }
Ari Saha89831742015-06-26 10:31:48 -0700315 handleRadiusPacket(radiusPacket);
316 }
317 break;
318 default:
319 return;
320 }
321 }
322
323
324 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700325 * Handles PAE packets (supplicant).
326 *
327 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700328 */
Jonathan Harta46dddf2015-06-30 15:31:20 -0700329 private void handleSupplicantPacket(InboundPacket inPacket) {
330 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700331 // Where does it come from?
332 MacAddress srcMAC = ethPkt.getSourceMAC();
333
Jonathan Harta46dddf2015-06-30 15:31:20 -0700334 DeviceId deviceId = inPacket.receivedFrom().deviceId();
335 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700336 String sessionId = deviceId.toString() + portNumber.toString();
337 StateMachine stateMachine = getStateMachine(sessionId);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700338
339 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700340
341 switch (eapol.getEapolType()) {
342 case EAPOL.EAPOL_START:
343 try {
344 stateMachine.start();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700345 stateMachine.supplicantConnectpoint = inPacket.receivedFrom();
Ari Saha89831742015-06-26 10:31:48 -0700346
347 //send an EAP Request/Identify to the supplicant
348 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700349 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
350 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
351 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700352 stateMachine.supplicantAddress = srcMAC;
353 stateMachine.vlanId = ethPkt.getVlanID();
354
355 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
356 } catch (StateMachineException e) {
357 e.printStackTrace();
358 }
359
360 break;
361 case EAPOL.EAPOL_PACKET:
Jonathan Harta46dddf2015-06-30 15:31:20 -0700362 //check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700363 EAP eapPacket = (EAP) eapol.getPayload();
364
365 byte dataType = eapPacket.getDataType();
366 switch (dataType) {
367 case EAP.ATTR_IDENTITY:
368 try {
369 //request id access to RADIUS
370 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700371 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700372 radiusPayload.setIdentifier(stateMachine.getIdentifier());
373 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700374 eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700375 stateMachine.setUsername(eapPacket.getData());
376 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700377 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700378
379 radiusPayload.encapsulateMessage(eapPacket);
380
381 // set Request Authenticator in StateMachine
382 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
383 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
384 sendRadiusMessage(radiusPayload);
385
386 //change the state to "PENDING"
387 stateMachine.requestAccess();
388 } catch (StateMachineException e) {
389 e.printStackTrace();
390 }
391 break;
392 case EAP.ATTR_MD5:
393 //verify if the EAP identifier corresponds to the challenge identifier from the client state
394 //machine.
395 if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
396 //send the RADIUS challenge response
397 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700398 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700399 radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
400 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700401 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700402 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700403 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700404
405 radiusPayload.encapsulateMessage(eapPacket);
406
407 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700408 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700409 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
410 sendRadiusMessage(radiusPayload);
411 }
412 break;
413 case EAP.ATTR_TLS:
414 try {
415 //request id access to RADIUS
416 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700417 eapPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700418 radiusPayload.setIdentifier(stateMachine.getIdentifier());
419 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700420 stateMachine.getUsername());
Ari Saha89831742015-06-26 10:31:48 -0700421 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700422 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700423
424 radiusPayload.encapsulateMessage(eapPacket);
425
426 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700427 stateMachine.getChallengeState());
Ari Saha89831742015-06-26 10:31:48 -0700428 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
429
430 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
431
432 sendRadiusMessage(radiusPayload);
433 // TODO: this gets called on every fragment, should only be called at TLS-Start
434 stateMachine.requestAccess();
435 } catch (StateMachineException e) {
436 e.printStackTrace();
437 }
438 break;
439 default:
440 return;
441 }
442 break;
443 default:
444 return;
445 }
446 }
447
448 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700449 * Handles RADIUS packets.
450 *
Ari Saha89831742015-06-26 10:31:48 -0700451 * @param radiusPacket RADIUS packet coming from the RADIUS server.
452 */
453 private void handleRadiusPacket(RADIUS radiusPacket) {
454 StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
455 if (stateMachine == null) {
456 log.error("Invalid session identifier, exiting...");
457 return;
458 }
459
Ari Saha89831742015-06-26 10:31:48 -0700460 EAP eapPayload = new EAP();
461 Ethernet eth = null;
462 switch (radiusPacket.getCode()) {
463 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
464 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
465 eapPayload = radiusPacket.decapsulateMessage();
466 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700467 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700468 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
469 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700470 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
471 break;
472 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
473 try {
474 //send an EAPOL - Success to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700475 byte[] eapMessage =
Ari Saha89831742015-06-26 10:31:48 -0700476 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
477 eapPayload = new EAP();
478 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700479 eth = buildEapolResponse(stateMachine.supplicantAddress,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700480 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
481 eapPayload);
Ari Saha89831742015-06-26 10:31:48 -0700482 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
483
484 stateMachine.authorizeAccess();
485 } catch (StateMachineException e) {
486 e.printStackTrace();
487 }
488 break;
489 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
490 try {
491 stateMachine.denyAccess();
492 } catch (StateMachineException e) {
493 e.printStackTrace();
494 }
495 break;
496 default:
497 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
498 }
499 }
500
501 private StateMachine getStateMachineById(byte identifier) {
502 StateMachine stateMachine = null;
503 Set stateMachineSet = stateMachineMap.entrySet();
504
505 synchronized (stateMachineMap) {
506 Iterator itr = stateMachineSet.iterator();
507 while (itr.hasNext()) {
508 Map.Entry entry = (Map.Entry) itr.next();
509 stateMachine = (StateMachine) entry.getValue();
510 if (identifier == stateMachine.getIdentifier()) {
511 //the state machine has already been created for this session session
512 stateMachine = (StateMachine) entry.getValue();
513 break;
514 }
515 }
516 }
517
518 return stateMachine;
519 }
520
521 private StateMachine getStateMachine(String sessionId) {
522 StateMachine stateMachine = null;
523 Set stateMachineSet = stateMachineMap.entrySet();
524
525 synchronized (stateMachineMap) {
526 Iterator itr = stateMachineSet.iterator();
527 while (itr.hasNext()) {
528
529 Map.Entry entry = (Map.Entry) itr.next();
530 if (sessionId.equals(entry.getKey())) {
531 //the state machine has already been created for this session session
532 stateMachine = (StateMachine) entry.getValue();
533 break;
534 }
535 }
536 }
537
538 if (stateMachine == null) {
539 stateMachine = new StateMachine(sessionId, voltTenantService);
540 stateMachineMap.put(sessionId, stateMachine);
541 }
542
543 return stateMachine;
544 }
545
546 private void sendRadiusMessage(RADIUS radiusMessage) {
547 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
548 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
549
550 Host dst;
551 if (!odst.isPresent()) {
552 log.info("Radius server {} is not present", radiusIpAddress);
553 return;
554 } else {
555 dst = odst.get();
556 }
557
558 UDP udp = new UDP();
559 IPv4 ip4Packet = new IPv4();
560 Ethernet ethPkt = new Ethernet();
561 radiusMessage.setParent(udp);
562 udp.setDestinationPort((short) 1812);
563 udp.setSourcePort((short) 1812); // TODO: make this configurable
564 udp.setPayload(radiusMessage);
565 udp.setParent(ip4Packet);
566 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
567 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
568 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
569 ip4Packet.setPayload(udp);
570 ip4Packet.setParent(ethPkt);
571 ethPkt.setDestinationMACAddress(radiusMacAddress);
572 ethPkt.setSourceMACAddress(nasMacAddress);
573 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
574 ethPkt.setPayload(ip4Packet);
575
576 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
577 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
578 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700579 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700580 packetService.emit(packet);
581
582 }
583
Ari Saha89831742015-06-26 10:31:48 -0700584 /**
585 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700586 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700587 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700588 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700589 */
590 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
591 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
592 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700593 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700594 packetService.emit(packet);
595 }
596
597 }
598
599}