blob: f5c6afdd474b03db193cbde8446499d589cb5e7a [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")
139 private 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")
Ari Saha89831742015-06-26 10:31:48 -0700143 private 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")
147 private 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")
Ari Saha89831742015-06-26 10:31:48 -0700151 private 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")
155 private 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
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 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700346 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700347 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();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700354 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
355 if (stateMachine == null) {
356 stateMachine = new StateMachine(sessionId, voltTenantService);
357 }
358
Jonathan Harta46dddf2015-06-30 15:31:20 -0700359
360 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700361
362 switch (eapol.getEapolType()) {
363 case EAPOL.EAPOL_START:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700364 stateMachine.start();
365 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Ari Saha89831742015-06-26 10:31:48 -0700366
Ray Milkeyf51eba22015-09-25 10:24:23 -0700367 //send an EAP Request/Identify to the supplicant
368 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
369 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
370 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
371 eapPayload);
372 stateMachine.setSupplicantAddress(srcMAC);
373 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700374
Ray Milkeyf51eba22015-09-25 10:24:23 -0700375 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700376
377 break;
378 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700379 RADIUS radiusPayload;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700380 //check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700381 EAP eapPacket = (EAP) eapol.getPayload();
382
383 byte dataType = eapPacket.getDataType();
384 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700385
Ray Milkeyf51eba22015-09-25 10:24:23 -0700386 case EAP.ATTR_IDENTITY:
387 //request id access to RADIUS
388 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700389
Ray Milkeyf51eba22015-09-25 10:24:23 -0700390 radiusPayload =
391 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
392 eapPacket.getIdentifier());
393 radiusPayload.setIdentifier(stateMachine.identifier());
394 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
395 eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700396
Ray Milkeyf51eba22015-09-25 10:24:23 -0700397 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
398 AAA.this.parsedNasIpAddress.getAddress());
399
400 radiusPayload.encapsulateMessage(eapPacket);
401
402 // set Request Authenticator in StateMachine
403 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
404 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
405 sendRadiusMessage(radiusPayload);
406
407 //change the state to "PENDING"
408 stateMachine.requestAccess();
409 break;
Ari Saha89831742015-06-26 10:31:48 -0700410 case EAP.ATTR_MD5:
411 //verify if the EAP identifier corresponds to the challenge identifier from the client state
412 //machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700413 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700414 //send the RADIUS challenge response
Ray Milkeyf51eba22015-09-25 10:24:23 -0700415 radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
416 eapPacket.getIdentifier());
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700417 radiusPayload.setIdentifier(stateMachine.challengeIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700418 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700419 stateMachine.username());
Ari Saha89831742015-06-26 10:31:48 -0700420 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700421 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700422
423 radiusPayload.encapsulateMessage(eapPacket);
424
425 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700426 stateMachine.challengeState());
Ari Saha89831742015-06-26 10:31:48 -0700427 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
428 sendRadiusMessage(radiusPayload);
429 }
430 break;
431 case EAP.ATTR_TLS:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700432 //request id access to RADIUS
433 radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
434 eapPacket.getIdentifier());
435 radiusPayload.setIdentifier(stateMachine.identifier());
436 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
437 stateMachine.username());
438 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
439 AAA.this.parsedNasIpAddress.getAddress());
Ari Saha89831742015-06-26 10:31:48 -0700440
Ray Milkeyf51eba22015-09-25 10:24:23 -0700441 radiusPayload.encapsulateMessage(eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700442
Ray Milkeyf51eba22015-09-25 10:24:23 -0700443 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
444 stateMachine.challengeState());
445 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700446
Ray Milkeyf51eba22015-09-25 10:24:23 -0700447 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
Ari Saha89831742015-06-26 10:31:48 -0700448
Ray Milkeyf51eba22015-09-25 10:24:23 -0700449 sendRadiusMessage(radiusPayload);
450 // TODO: this gets called on every fragment, should only be called at TLS-Start
451 stateMachine.requestAccess();
452
Ari Saha89831742015-06-26 10:31:48 -0700453 break;
454 default:
455 return;
456 }
457 break;
458 default:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700459 log.trace("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700460 }
461 }
462
463 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700464 * Handles RADIUS packets.
465 *
Ari Saha89831742015-06-26 10:31:48 -0700466 * @param radiusPacket RADIUS packet coming from the RADIUS server.
467 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700468 private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700469 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700470 if (stateMachine == null) {
471 log.error("Invalid session identifier, exiting...");
472 return;
473 }
474
Ray Milkeyf51eba22015-09-25 10:24:23 -0700475 EAP eapPayload;
476 Ethernet eth;
Ari Saha89831742015-06-26 10:31:48 -0700477 switch (radiusPacket.getCode()) {
478 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
479 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
480 eapPayload = radiusPacket.decapsulateMessage();
481 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700482 eth = buildEapolResponse(stateMachine.supplicantAddress(),
483 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700484 eapPayload);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700485 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700486 break;
487 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700488 //send an EAPOL - Success to the supplicant.
489 byte[] eapMessage =
490 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
491 eapPayload = new EAP();
492 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
493 eth = buildEapolResponse(stateMachine.supplicantAddress(),
494 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
495 eapPayload);
496 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700497
Ray Milkeyf51eba22015-09-25 10:24:23 -0700498 stateMachine.authorizeAccess();
Ari Saha89831742015-06-26 10:31:48 -0700499 break;
500 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700501 stateMachine.denyAccess();
Ari Saha89831742015-06-26 10:31:48 -0700502 break;
503 default:
504 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
505 }
506 }
507
Ari Saha89831742015-06-26 10:31:48 -0700508 private void sendRadiusMessage(RADIUS radiusMessage) {
509 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
510 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
511
512 Host dst;
513 if (!odst.isPresent()) {
514 log.info("Radius server {} is not present", radiusIpAddress);
515 return;
516 } else {
517 dst = odst.get();
518 }
519
520 UDP udp = new UDP();
521 IPv4 ip4Packet = new IPv4();
522 Ethernet ethPkt = new Ethernet();
523 radiusMessage.setParent(udp);
524 udp.setDestinationPort((short) 1812);
525 udp.setSourcePort((short) 1812); // TODO: make this configurable
526 udp.setPayload(radiusMessage);
527 udp.setParent(ip4Packet);
528 ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
529 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
530 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
531 ip4Packet.setPayload(udp);
532 ip4Packet.setParent(ethPkt);
533 ethPkt.setDestinationMACAddress(radiusMacAddress);
534 ethPkt.setSourceMACAddress(nasMacAddress);
535 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
536 ethPkt.setPayload(ip4Packet);
537
538 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
539 .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
540 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700541 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700542 packetService.emit(packet);
543
544 }
545
Ari Saha89831742015-06-26 10:31:48 -0700546 /**
547 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700548 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700549 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700550 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700551 */
552 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
553 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
554 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700555 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700556 packetService.emit(packet);
557 }
558
559 }
560
561}