blob: e1a485f8b71bc9295cc20c46524d2dfd083644df [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";
Ray Milkeyf51eba22015-09-25 10:24:23 -070082
Ari Saha89831742015-06-26 10:31:48 -070083 // NAS IP address
84 private static final String DEFAULT_NAS_IP = "192.168.1.11";
Ray Milkeyf51eba22015-09-25 10:24:23 -070085
Ari Saha89831742015-06-26 10:31:48 -070086 // RADIUS uplink port
87 private static final int DEFAULT_RADIUS_UPLINK = 2;
Ray Milkeyf51eba22015-09-25 10:24:23 -070088
Ari Saha89831742015-06-26 10:31:48 -070089 // RADIUS server shared secret
90 private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
Ray Milkeyf51eba22015-09-25 10:24:23 -070091
Jonathan Harta46dddf2015-06-30 15:31:20 -070092 // RADIUS MAC address
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070093 private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
Ray Milkeyf51eba22015-09-25 10:24:23 -070094
Jonathan Harta46dddf2015-06-30 15:31:20 -070095 // NAS MAC address
Ari Saha89831742015-06-26 10:31:48 -070096 private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
Ray Milkeyf51eba22015-09-25 10:24:23 -070097
Jonathan Harta46dddf2015-06-30 15:31:20 -070098 // Radius Switch Id
alshabib32039d52015-07-01 16:35:26 -070099 private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
Ray Milkeyf51eba22015-09-25 10:24:23 -0700100
Jonathan Harta46dddf2015-06-30 15:31:20 -0700101 // Radius Port Number
alshabib32039d52015-07-01 16:35:26 -0700102 private static final String DEFAULT_RADIUS_PORT = "129";
Ray Milkeyf51eba22015-09-25 10:24:23 -0700103
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700104 // for verbose output
105 private final Logger log = getLogger(getClass());
Ray Milkeyf51eba22015-09-25 10:24:23 -0700106
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700107 // a list of our dependencies :
108 // to register with ONOS as an application - described next
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected CoreService coreService;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700111
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700112 // to receive Packet-in events that we'll respond to
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected PacketService packetService;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700115
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700116 // end host information
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected HostService hostService;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700119
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected VoltTenantService voltTenantService;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected ComponentConfigService cfgService;
125
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700126 // Parsed RADIUS server IP address
127 protected InetAddress parsedRadiusIpAddress;
128 // Parsed NAS IP address
129 protected InetAddress parsedNasIpAddress;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700130
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700131 // our application-specific event handler
132 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
Ray Milkeyf51eba22015-09-25 10:24:23 -0700133
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700134 // our unique identifier
135 private ApplicationId appId;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700136
Ari Saha89831742015-06-26 10:31:48 -0700137 @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
138 label = "RADIUS IP Address")
Ray Milkeyea366452015-09-30 10:56:43 -0700139 protected String radiusIpAddress = DEFAULT_RADIUS_IP;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700140
Ari Saha89831742015-06-26 10:31:48 -0700141 @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700142 label = "NAS IP Address")
Ray Milkeyea366452015-09-30 10:56:43 -0700143 protected String nasIpAddress = DEFAULT_NAS_IP;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700144
Ari Saha89831742015-06-26 10:31:48 -0700145 @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
146 label = "RADIUS MAC Address")
Ray Milkeyea366452015-09-30 10:56:43 -0700147 protected String radiusMacAddress = RADIUS_MAC_ADDRESS;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700148
Ari Saha89831742015-06-26 10:31:48 -0700149 @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700150 label = "NAS MAC Address")
Ray Milkeyea366452015-09-30 10:56:43 -0700151 protected String nasMacAddress = NAS_MAC_ADDRESS;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700152
Ari Saha89831742015-06-26 10:31:48 -0700153 @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
154 label = "RADIUS shared secret")
Ray Milkeyea366452015-09-30 10:56:43 -0700155 protected String radiusSecret = DEFAULT_RADIUS_SECRET;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700156
Ari Saha89831742015-06-26 10:31:48 -0700157 @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
158 label = "Radius switch")
159 private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700160
Ari Saha89831742015-06-26 10:31:48 -0700161 @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
162 label = "Radius port")
163 private String radiusPort = DEFAULT_RADIUS_PORT;
164
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700165 /**
166 * Builds an EAPOL packet based on the given parameters.
167 *
168 * @param dstMac destination MAC address
169 * @param srcMac source MAC address
170 * @param vlan vlan identifier
171 * @param eapolType EAPOL type
172 * @param eap EAP payload
173 * @return Ethernet frame
174 */
175 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
176 short vlan, byte eapolType, EAP eap) {
Ari Saha89831742015-06-26 10:31:48 -0700177
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700178 Ethernet eth = new Ethernet();
179 eth.setDestinationMACAddress(dstMac.toBytes());
180 eth.setSourceMACAddress(srcMac.toBytes());
181 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
182 if (vlan != Ethernet.VLAN_UNTAGGED) {
183 eth.setVlanID(vlan);
184 }
185 //eapol header
186 EAPOL eapol = new EAPOL();
187 eapol.setEapolType(eapolType);
188 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700189
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700190 //eap part
191 eapol.setPayload(eap);
192
193 eth.setPayload(eapol);
194 eth.setPad(true);
195 return eth;
196 }
Ari Saha89831742015-06-26 10:31:48 -0700197
198 @Modified
199 public void modified(ComponentContext context) {
200 Dictionary<?, ?> properties = context.getProperties();
201
202 String s = Tools.get(properties, "radiusIpAddress");
203 try {
204 parsedRadiusIpAddress = InetAddress.getByName(s);
205 radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
206 } catch (UnknownHostException e) {
Ray Milkeyf51eba22015-09-25 10:24:23 -0700207 log.error("Invalid RADIUS IP address specification: {}", s, e);
Ari Saha89831742015-06-26 10:31:48 -0700208 }
209 try {
210 s = Tools.get(properties, "nasIpAddress");
211 parsedNasIpAddress = InetAddress.getByName(s);
212 nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
213 } catch (UnknownHostException e) {
Ray Milkeyf51eba22015-09-25 10:24:23 -0700214 log.error("Invalid NAS IP address specification: {}", s, e);
Ari Saha89831742015-06-26 10:31:48 -0700215 }
216
217 s = Tools.get(properties, "radiusMacAddress");
218 radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
219
220 s = Tools.get(properties, "nasMacAddress");
221 nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
222
223 s = Tools.get(properties, "radiusSecret");
224 radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
225
226 s = Tools.get(properties, "radiusSwitchId");
227 radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
228
229 s = Tools.get(properties, "radiusPortNumber");
230 radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
231 }
232
233 @Activate
234 public void activate(ComponentContext context) {
235 cfgService.registerProperties(getClass());
236 modified(context);
237 // "org.onosproject.aaa" is the FQDN of our app
238 appId = coreService.registerApplication("org.onosproject.aaa");
239 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700240 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700241 requestIntercepts();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700242
243 StateMachine.initializeMaps();
Ari Saha89831742015-06-26 10:31:48 -0700244
245 hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
Ari Saha89831742015-06-26 10:31:48 -0700246 }
247
248 @Deactivate
249 public void deactivate() {
250 cfgService.unregisterProperties(getClass(), false);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700251
252 appId = coreService.registerApplication("org.onosproject.aaa");
253 withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700254 // de-register and null our handler
255 packetService.removeProcessor(processor);
256 processor = null;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700257 StateMachine.destroyMaps();
Ari Saha89831742015-06-26 10:31:48 -0700258 }
259
Jonathan Harta46dddf2015-06-30 15:31:20 -0700260 /**
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700261 * Request packet in via PacketService.
262 */
263 private void requestIntercepts() {
264 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
265 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
266 packetService.requestPackets(selector.build(),
267 CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700268
269 TrafficSelector radSelector = DefaultTrafficSelector.builder()
270 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
271 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700272 .matchUdpDst(TpPort.tpPort(1812))
273 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700274 .build();
275 packetService.requestPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700276 }
277
278 /**
279 * Cancel request for packet in via PacketService.
280 */
281 private void withdrawIntercepts() {
282 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
283 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
284 packetService.cancelPackets(selector.build(), CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700285
286 TrafficSelector radSelector = DefaultTrafficSelector.builder()
287 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
288 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700289 .matchUdpDst(TpPort.tpPort(1812))
290 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700291 .build();
292 packetService.cancelPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700293 }
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 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700310 try {
311 // identify if incoming packet comes from supplicant (EAP) or RADIUS
312 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
313 case EAPOL:
314 handleSupplicantPacket(context.inPacket());
315 break;
316 case IPV4:
317 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
Ray Milkeyf51eba22015-09-25 10:24:23 -0700324 byte[] datagram = udpPacket.getPayload().serialize();
325 RADIUS radiusPacket;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700326 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700327 handleRadiusPacket(radiusPacket);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700328 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700329
330 break;
331 default:
332 log.trace("Skipping Ethernet packet type {}",
333 EthType.EtherType.lookup(ethPkt.getEtherType()));
334 }
335 } catch (DeserializationException | StateMachineException e) {
336 log.warn("Unable to process RADIUS packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700337 }
338 }
339
Ray Milkey9eb293f2015-09-30 15:09:17 -0700340 /**
341 * Creates and initializes common fields of a RADIUS packet.
342 *
343 * @param identifier RADIUS identifier
344 * @param eapPacket EAP packet
345 * @return RADIUS packet
346 */
347 private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) {
348 RADIUS radiusPayload =
349 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
350 eapPacket.getIdentifier());
351 radiusPayload.setIdentifier(identifier);
352 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
353 eapPacket.getData());
354
355 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
356 AAA.this.parsedNasIpAddress.getAddress());
357
358 radiusPayload.encapsulateMessage(eapPacket);
359 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
360
361 return radiusPayload;
362 }
Ari Saha89831742015-06-26 10:31:48 -0700363
364 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700365 * Handles PAE packets (supplicant).
366 *
367 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700368 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700369 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700370 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700371 // Where does it come from?
372 MacAddress srcMAC = ethPkt.getSourceMAC();
373
Jonathan Harta46dddf2015-06-30 15:31:20 -0700374 DeviceId deviceId = inPacket.receivedFrom().deviceId();
375 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700376 String sessionId = deviceId.toString() + portNumber.toString();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700377 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
378 if (stateMachine == null) {
379 stateMachine = new StateMachine(sessionId, voltTenantService);
380 }
381
Jonathan Harta46dddf2015-06-30 15:31:20 -0700382
383 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700384
385 switch (eapol.getEapolType()) {
386 case EAPOL.EAPOL_START:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700387 stateMachine.start();
388 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Ari Saha89831742015-06-26 10:31:48 -0700389
Ray Milkeyf51eba22015-09-25 10:24:23 -0700390 //send an EAP Request/Identify to the supplicant
391 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
392 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
393 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
394 eapPayload);
395 stateMachine.setSupplicantAddress(srcMAC);
396 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700397
Ray Milkeyf51eba22015-09-25 10:24:23 -0700398 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700399
400 break;
401 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700402 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700403 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700404 EAP eapPacket = (EAP) eapol.getPayload();
405
406 byte dataType = eapPacket.getDataType();
407 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700408
Ray Milkey9eb293f2015-09-30 15:09:17 -0700409 case EAP.ATTR_IDENTITY:
410 // request id access to RADIUS
411 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700412
Ray Milkey9eb293f2015-09-30 15:09:17 -0700413 radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700414
Ray Milkey9eb293f2015-09-30 15:09:17 -0700415 // set Request Authenticator in StateMachine
416 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
417 sendRadiusMessage(radiusPayload);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700418
Ray Milkey9eb293f2015-09-30 15:09:17 -0700419 // change the state to "PENDING"
420 stateMachine.requestAccess();
421 break;
Ari Saha89831742015-06-26 10:31:48 -0700422 case EAP.ATTR_MD5:
Ray Milkey9eb293f2015-09-30 15:09:17 -0700423 // verify if the EAP identifier corresponds to the
424 // challenge identifier from the client state
425 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700426 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700427 //send the RADIUS challenge response
Ray Milkey9eb293f2015-09-30 15:09:17 -0700428 radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700429
430 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700431 stateMachine.challengeState());
Ari Saha89831742015-06-26 10:31:48 -0700432 sendRadiusMessage(radiusPayload);
433 }
434 break;
435 case EAP.ATTR_TLS:
Ray Milkey9eb293f2015-09-30 15:09:17 -0700436 // request id access to RADIUS
437 radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700438
Ray Milkeyf51eba22015-09-25 10:24:23 -0700439 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
440 stateMachine.challengeState());
441 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700442
Ray Milkeyf51eba22015-09-25 10:24:23 -0700443 sendRadiusMessage(radiusPayload);
444 // TODO: this gets called on every fragment, should only be called at TLS-Start
445 stateMachine.requestAccess();
446
Ari Saha89831742015-06-26 10:31:48 -0700447 break;
448 default:
449 return;
450 }
451 break;
452 default:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700453 log.trace("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700454 }
455 }
456
457 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700458 * Handles RADIUS packets.
459 *
Ari Saha89831742015-06-26 10:31:48 -0700460 * @param radiusPacket RADIUS packet coming from the RADIUS server.
461 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700462 private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700463 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700464 if (stateMachine == null) {
465 log.error("Invalid session identifier, exiting...");
466 return;
467 }
468
Ray Milkeyf51eba22015-09-25 10:24:23 -0700469 EAP eapPayload;
470 Ethernet eth;
Ari Saha89831742015-06-26 10:31:48 -0700471 switch (radiusPacket.getCode()) {
472 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
473 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
474 eapPayload = radiusPacket.decapsulateMessage();
475 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
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 break;
481 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700482 //send an EAPOL - Success to the supplicant.
483 byte[] eapMessage =
484 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
485 eapPayload = new EAP();
486 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
487 eth = buildEapolResponse(stateMachine.supplicantAddress(),
488 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
489 eapPayload);
490 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700491
Ray Milkeyf51eba22015-09-25 10:24:23 -0700492 stateMachine.authorizeAccess();
Ari Saha89831742015-06-26 10:31:48 -0700493 break;
494 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700495 stateMachine.denyAccess();
Ari Saha89831742015-06-26 10:31:48 -0700496 break;
497 default:
498 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
499 }
500 }
501
Ari Saha89831742015-06-26 10:31:48 -0700502 private void sendRadiusMessage(RADIUS radiusMessage) {
503 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
504 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
505
506 Host dst;
507 if (!odst.isPresent()) {
508 log.info("Radius server {} is not present", radiusIpAddress);
509 return;
510 } else {
511 dst = odst.get();
512 }
513
514 UDP udp = new UDP();
515 IPv4 ip4Packet = new IPv4();
516 Ethernet ethPkt = new Ethernet();
517 radiusMessage.setParent(udp);
518 udp.setDestinationPort((short) 1812);
519 udp.setSourcePort((short) 1812); // TODO: make this configurable
520 udp.setPayload(radiusMessage);
521 udp.setParent(ip4Packet);
522 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
523 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
524 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
525 ip4Packet.setPayload(udp);
526 ip4Packet.setParent(ethPkt);
527 ethPkt.setDestinationMACAddress(radiusMacAddress);
528 ethPkt.setSourceMACAddress(nasMacAddress);
529 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
530 ethPkt.setPayload(ip4Packet);
531
532 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
533 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
534 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700535 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700536 packetService.emit(packet);
537
538 }
539
Ari Saha89831742015-06-26 10:31:48 -0700540 /**
541 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700542 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700543 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700544 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700545 */
546 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
547 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
548 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700549 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700550 packetService.emit(packet);
551 }
552
553 }
554
555}