blob: e2656684f2423e22e981cfb73d0ae92f896db7f8 [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
Ray Milkeyf61a24e2015-09-24 16:34:02 -070018import java.net.InetAddress;
19import java.net.UnknownHostException;
20import java.nio.ByteBuffer;
21import java.util.Dictionary;
22import java.util.Optional;
23import java.util.Set;
24
Ari Saha89831742015-06-26 10:31:48 -070025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Modified;
29import org.apache.felix.scr.annotations.Property;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harta46dddf2015-06-30 15:31:20 -070032import org.onlab.packet.DeserializationException;
33import org.onlab.packet.EAP;
34import org.onlab.packet.EAPOL;
35import org.onlab.packet.EthType;
Ari Saha89831742015-06-26 10:31:48 -070036import org.onlab.packet.Ethernet;
37import org.onlab.packet.IPv4;
38import org.onlab.packet.Ip4Address;
39import org.onlab.packet.IpAddress;
40import org.onlab.packet.MacAddress;
Jonathan Harta46dddf2015-06-30 15:31:20 -070041import org.onlab.packet.RADIUS;
42import org.onlab.packet.RADIUSAttribute;
Hyunsun Moona0d63712015-08-22 21:04:23 -070043import org.onlab.packet.TpPort;
Ari Saha89831742015-06-26 10:31:48 -070044import org.onlab.packet.UDP;
45import org.onlab.packet.VlanId;
46import org.onlab.util.Tools;
Ari Saha89831742015-06-26 10:31:48 -070047import org.onosproject.cfg.ComponentConfigService;
48import org.onosproject.core.ApplicationId;
49import org.onosproject.core.CoreService;
50import org.onosproject.net.ConnectPoint;
51import org.onosproject.net.DeviceId;
52import org.onosproject.net.Host;
53import org.onosproject.net.PortNumber;
54import org.onosproject.net.flow.DefaultTrafficSelector;
55import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070056import org.onosproject.net.flow.TrafficSelector;
57import org.onosproject.net.flow.TrafficTreatment;
58import org.onosproject.net.host.HostService;
Ari Saha89831742015-06-26 10:31:48 -070059import org.onosproject.net.packet.DefaultOutboundPacket;
60import org.onosproject.net.packet.InboundPacket;
61import org.onosproject.net.packet.OutboundPacket;
62import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070063import org.onosproject.net.packet.PacketProcessor;
64import org.onosproject.net.packet.PacketService;
Ari Saha89831742015-06-26 10:31:48 -070065import org.onosproject.xosintegration.VoltTenantService;
66import org.osgi.service.component.ComponentContext;
67import org.slf4j.Logger;
68
Ray Milkeyf61a24e2015-09-24 16:34:02 -070069import com.google.common.base.Strings;
Ari Saha89831742015-06-26 10:31:48 -070070
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070071import static org.onosproject.net.packet.PacketPriority.CONTROL;
Ari Saha89831742015-06-26 10:31:48 -070072import static org.slf4j.LoggerFactory.getLogger;
73
74
75/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070076 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070077 */
78@Component(immediate = true)
79public class AAA {
Ari Saha89831742015-06-26 10:31:48 -070080 // RADIUS server IP address
81 private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
82 // NAS IP address
83 private static final String DEFAULT_NAS_IP = "192.168.1.11";
84 // RADIUS uplink port
85 private static final int DEFAULT_RADIUS_UPLINK = 2;
86 // RADIUS server shared secret
87 private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
Jonathan Harta46dddf2015-06-30 15:31:20 -070088 // RADIUS MAC address
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070089 private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
Jonathan Harta46dddf2015-06-30 15:31:20 -070090 // NAS MAC address
Ari Saha89831742015-06-26 10:31:48 -070091 private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
Jonathan Harta46dddf2015-06-30 15:31:20 -070092 // Radius Switch Id
alshabib32039d52015-07-01 16:35:26 -070093 private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
Jonathan Harta46dddf2015-06-30 15:31:20 -070094 // Radius Port Number
alshabib32039d52015-07-01 16:35:26 -070095 private static final String DEFAULT_RADIUS_PORT = "129";
Ray Milkeyf61a24e2015-09-24 16:34:02 -070096 // for verbose output
97 private final Logger log = getLogger(getClass());
98 // a list of our dependencies :
99 // to register with ONOS as an application - described next
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102 // to receive Packet-in events that we'll respond to
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected PacketService packetService;
105 // end host information
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected HostService hostService;
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected VoltTenantService voltTenantService;
110 // Parsed RADIUS server IP address
111 protected InetAddress parsedRadiusIpAddress;
112 // Parsed NAS IP address
113 protected InetAddress parsedNasIpAddress;
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ComponentConfigService cfgService;
116 // our application-specific event handler
117 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
118 // our unique identifier
119 private ApplicationId appId;
Ari Saha89831742015-06-26 10:31:48 -0700120 @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
121 label = "RADIUS IP Address")
122 private String radiusIpAddress = DEFAULT_RADIUS_IP;
Ari Saha89831742015-06-26 10:31:48 -0700123 @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700124 label = "NAS IP Address")
Ari Saha89831742015-06-26 10:31:48 -0700125 private String nasIpAddress = DEFAULT_NAS_IP;
Ari Saha89831742015-06-26 10:31:48 -0700126 @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
127 label = "RADIUS MAC Address")
128 private String radiusMacAddress = RADIUS_MAC_ADDRESS;
Ari Saha89831742015-06-26 10:31:48 -0700129 @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700130 label = "NAS MAC Address")
Ari Saha89831742015-06-26 10:31:48 -0700131 private String nasMacAddress = NAS_MAC_ADDRESS;
Ari Saha89831742015-06-26 10:31:48 -0700132 @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
133 label = "RADIUS shared secret")
134 private String radiusSecret = DEFAULT_RADIUS_SECRET;
Ari Saha89831742015-06-26 10:31:48 -0700135 @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
136 label = "Radius switch")
137 private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
Ari Saha89831742015-06-26 10:31:48 -0700138 @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
139 label = "Radius port")
140 private String radiusPort = DEFAULT_RADIUS_PORT;
141
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700142 /**
143 * Builds an EAPOL packet based on the given parameters.
144 *
145 * @param dstMac destination MAC address
146 * @param srcMac source MAC address
147 * @param vlan vlan identifier
148 * @param eapolType EAPOL type
149 * @param eap EAP payload
150 * @return Ethernet frame
151 */
152 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
153 short vlan, byte eapolType, EAP eap) {
Ari Saha89831742015-06-26 10:31:48 -0700154
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700155 Ethernet eth = new Ethernet();
156 eth.setDestinationMACAddress(dstMac.toBytes());
157 eth.setSourceMACAddress(srcMac.toBytes());
158 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
159 if (vlan != Ethernet.VLAN_UNTAGGED) {
160 eth.setVlanID(vlan);
161 }
162 //eapol header
163 EAPOL eapol = new EAPOL();
164 eapol.setEapolType(eapolType);
165 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700166
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700167 //eap part
168 eapol.setPayload(eap);
169
170 eth.setPayload(eapol);
171 eth.setPad(true);
172 return eth;
173 }
Ari Saha89831742015-06-26 10:31:48 -0700174
175 @Modified
176 public void modified(ComponentContext context) {
177 Dictionary<?, ?> properties = context.getProperties();
178
179 String s = Tools.get(properties, "radiusIpAddress");
180 try {
181 parsedRadiusIpAddress = InetAddress.getByName(s);
182 radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
183 } catch (UnknownHostException e) {
184 log.error("Invalid RADIUS IP address specification: {}", s);
185 }
186 try {
187 s = Tools.get(properties, "nasIpAddress");
188 parsedNasIpAddress = InetAddress.getByName(s);
189 nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
190 } catch (UnknownHostException e) {
191 log.error("Invalid NAS IP address specification: {}", s);
192 }
193
194 s = Tools.get(properties, "radiusMacAddress");
195 radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
196
197 s = Tools.get(properties, "nasMacAddress");
198 nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
199
200 s = Tools.get(properties, "radiusSecret");
201 radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
202
203 s = Tools.get(properties, "radiusSwitchId");
204 radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
205
206 s = Tools.get(properties, "radiusPortNumber");
207 radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
208 }
209
210 @Activate
211 public void activate(ComponentContext context) {
212 cfgService.registerProperties(getClass());
213 modified(context);
214 // "org.onosproject.aaa" is the FQDN of our app
215 appId = coreService.registerApplication("org.onosproject.aaa");
216 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700217 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700218 requestIntercepts();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700219
220 StateMachine.initializeMaps();
Ari Saha89831742015-06-26 10:31:48 -0700221
222 hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
Ari Saha89831742015-06-26 10:31:48 -0700223 }
224
225 @Deactivate
226 public void deactivate() {
227 cfgService.unregisterProperties(getClass(), false);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700228
229 appId = coreService.registerApplication("org.onosproject.aaa");
230 withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700231 // de-register and null our handler
232 packetService.removeProcessor(processor);
233 processor = null;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700234 StateMachine.destroyMaps();
Ari Saha89831742015-06-26 10:31:48 -0700235 }
236
Jonathan Harta46dddf2015-06-30 15:31:20 -0700237 /**
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700238 * Request packet in via PacketService.
239 */
240 private void requestIntercepts() {
241 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
242 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
243 packetService.requestPackets(selector.build(),
244 CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700245
246 TrafficSelector radSelector = DefaultTrafficSelector.builder()
247 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
248 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700249 .matchUdpDst(TpPort.tpPort(1812))
250 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700251 .build();
252 packetService.requestPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700253 }
254
255 /**
256 * Cancel request for packet in via PacketService.
257 */
258 private void withdrawIntercepts() {
259 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
260 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
261 packetService.cancelPackets(selector.build(), CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700262
263 TrafficSelector radSelector = DefaultTrafficSelector.builder()
264 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
265 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700266 .matchUdpDst(TpPort.tpPort(1812))
267 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700268 .build();
269 packetService.cancelPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700270 }
271
Ari Saha89831742015-06-26 10:31:48 -0700272 // our handler defined as a private inner class
273
274 /**
275 * Packet processor responsible for forwarding packets along their paths.
276 */
277 private class ReactivePacketProcessor implements PacketProcessor {
278 @Override
279 public void process(PacketContext context) {
280
281 // Extract the original Ethernet frame from the packet information
282 InboundPacket pkt = context.inPacket();
283 Ethernet ethPkt = pkt.parsed();
284 if (ethPkt == null) {
285 return;
286 }
Jonathan Harta46dddf2015-06-30 15:31:20 -0700287 // identify if incoming packet comes from supplicant (EAP) or RADIUS
288 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
289 case EAPOL:
290 handleSupplicantPacket(context.inPacket());
Ari Saha89831742015-06-26 10:31:48 -0700291 break;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700292 case IPV4:
Ari Saha89831742015-06-26 10:31:48 -0700293 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
294 Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
295 Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
296 if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
297 // TODO: check for port as well when it's configurable
298 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700299
Ari Saha89831742015-06-26 10:31:48 -0700300 byte[] datagram = udpPacket.getPayload().serialize();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700301 RADIUS radiusPacket;
302 try {
303 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
304 } catch (DeserializationException e) {
305 log.warn("Unable to deserialize RADIUS packet:", e);
306 return;
307 }
Ari Saha89831742015-06-26 10:31:48 -0700308 handleRadiusPacket(radiusPacket);
309 }
310 break;
311 default:
312 return;
313 }
314 }
315
316
317 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700318 * Handles PAE packets (supplicant).
319 *
320 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700321 */
Jonathan Harta46dddf2015-06-30 15:31:20 -0700322 private void handleSupplicantPacket(InboundPacket inPacket) {
323 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700324 // Where does it come from?
325 MacAddress srcMAC = ethPkt.getSourceMAC();
326
Jonathan Harta46dddf2015-06-30 15:31:20 -0700327 DeviceId deviceId = inPacket.receivedFrom().deviceId();
328 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700329 String sessionId = deviceId.toString() + portNumber.toString();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700330 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
331 if (stateMachine == null) {
332 stateMachine = new StateMachine(sessionId, voltTenantService);
333 }
334
Jonathan Harta46dddf2015-06-30 15:31:20 -0700335
336 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700337
338 switch (eapol.getEapolType()) {
339 case EAPOL.EAPOL_START:
340 try {
341 stateMachine.start();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700342 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Ari Saha89831742015-06-26 10:31:48 -0700343
344 //send an EAP Request/Identify to the supplicant
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700345 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700346 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
347 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
348 eapPayload);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700349 stateMachine.setSupplicantAddress(srcMAC);
350 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700351
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700352 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700353 } catch (StateMachineException e) {
354 e.printStackTrace();
355 }
356
357 break;
358 case EAPOL.EAPOL_PACKET:
Jonathan Harta46dddf2015-06-30 15:31:20 -0700359 //check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700360 EAP eapPacket = (EAP) eapol.getPayload();
361
362 byte dataType = eapPacket.getDataType();
363 switch (dataType) {
364 case EAP.ATTR_IDENTITY:
365 try {
366 //request id access to RADIUS
367 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700368 eapPacket.getIdentifier());
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700369 radiusPayload.setIdentifier(stateMachine.identifier());
Ari Saha89831742015-06-26 10:31:48 -0700370 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700371 eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700372 stateMachine.setUsername(eapPacket.getData());
373 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700374 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700375
376 radiusPayload.encapsulateMessage(eapPacket);
377
378 // set Request Authenticator in StateMachine
379 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
380 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
381 sendRadiusMessage(radiusPayload);
382
383 //change the state to "PENDING"
384 stateMachine.requestAccess();
385 } catch (StateMachineException e) {
386 e.printStackTrace();
387 }
388 break;
389 case EAP.ATTR_MD5:
390 //verify if the EAP identifier corresponds to the challenge identifier from the client state
391 //machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700392 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700393 //send the RADIUS challenge response
394 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700395 eapPacket.getIdentifier());
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700396 radiusPayload.setIdentifier(stateMachine.challengeIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700397 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700398 stateMachine.username());
Ari Saha89831742015-06-26 10:31:48 -0700399 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700400 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700401
402 radiusPayload.encapsulateMessage(eapPacket);
403
404 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700405 stateMachine.challengeState());
Ari Saha89831742015-06-26 10:31:48 -0700406 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
407 sendRadiusMessage(radiusPayload);
408 }
409 break;
410 case EAP.ATTR_TLS:
411 try {
412 //request id access to RADIUS
413 RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700414 eapPacket.getIdentifier());
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700415 radiusPayload.setIdentifier(stateMachine.identifier());
Ari Saha89831742015-06-26 10:31:48 -0700416 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700417 stateMachine.username());
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,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700424 stateMachine.challengeState());
Ari Saha89831742015-06-26 10:31:48 -0700425 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
426
427 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
428
429 sendRadiusMessage(radiusPayload);
430 // TODO: this gets called on every fragment, should only be called at TLS-Start
431 stateMachine.requestAccess();
432 } catch (StateMachineException e) {
433 e.printStackTrace();
434 }
435 break;
436 default:
437 return;
438 }
439 break;
440 default:
441 return;
442 }
443 }
444
445 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700446 * Handles RADIUS packets.
447 *
Ari Saha89831742015-06-26 10:31:48 -0700448 * @param radiusPacket RADIUS packet coming from the RADIUS server.
449 */
450 private void handleRadiusPacket(RADIUS radiusPacket) {
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700451 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700452 if (stateMachine == null) {
453 log.error("Invalid session identifier, exiting...");
454 return;
455 }
456
Ari Saha89831742015-06-26 10:31:48 -0700457 EAP eapPayload = new EAP();
458 Ethernet eth = null;
459 switch (radiusPacket.getCode()) {
460 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
461 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
462 eapPayload = radiusPacket.decapsulateMessage();
463 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700464 eth = buildEapolResponse(stateMachine.supplicantAddress(),
465 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700466 eapPayload);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700467 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700468 break;
469 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
470 try {
471 //send an EAPOL - Success to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700472 byte[] eapMessage =
Ari Saha89831742015-06-26 10:31:48 -0700473 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
474 eapPayload = new EAP();
475 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700476 eth = buildEapolResponse(stateMachine.supplicantAddress(),
477 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700478 eapPayload);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700479 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700480
481 stateMachine.authorizeAccess();
482 } catch (StateMachineException e) {
483 e.printStackTrace();
484 }
485 break;
486 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
487 try {
488 stateMachine.denyAccess();
489 } catch (StateMachineException e) {
490 e.printStackTrace();
491 }
492 break;
493 default:
494 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
495 }
496 }
497
Ari Saha89831742015-06-26 10:31:48 -0700498 private void sendRadiusMessage(RADIUS radiusMessage) {
499 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
500 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
501
502 Host dst;
503 if (!odst.isPresent()) {
504 log.info("Radius server {} is not present", radiusIpAddress);
505 return;
506 } else {
507 dst = odst.get();
508 }
509
510 UDP udp = new UDP();
511 IPv4 ip4Packet = new IPv4();
512 Ethernet ethPkt = new Ethernet();
513 radiusMessage.setParent(udp);
514 udp.setDestinationPort((short) 1812);
515 udp.setSourcePort((short) 1812); // TODO: make this configurable
516 udp.setPayload(radiusMessage);
517 udp.setParent(ip4Packet);
518 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
519 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
520 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
521 ip4Packet.setPayload(udp);
522 ip4Packet.setParent(ethPkt);
523 ethPkt.setDestinationMACAddress(radiusMacAddress);
524 ethPkt.setSourceMACAddress(nasMacAddress);
525 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
526 ethPkt.setPayload(ip4Packet);
527
528 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
529 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
530 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700531 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700532 packetService.emit(packet);
533
534 }
535
Ari Saha89831742015-06-26 10:31:48 -0700536 /**
537 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700538 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700539 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700540 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700541 */
542 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
543 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
544 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700545 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700546 packetService.emit(packet);
547 }
548
549 }
550
551}