blob: 59042fe68878f2b2c4f9f2cc1276d5c921402d74 [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;
Ray Milkeyf61a24e2015-09-24 16:34:02 -070019import java.nio.ByteBuffer;
Ray Milkeyf61a24e2015-09-24 16:34:02 -070020import java.util.Optional;
21import java.util.Set;
22
Ari Saha89831742015-06-26 10:31:48 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
Ari Saha89831742015-06-26 10:31:48 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harta46dddf2015-06-30 15:31:20 -070028import org.onlab.packet.DeserializationException;
29import org.onlab.packet.EAP;
30import org.onlab.packet.EAPOL;
31import org.onlab.packet.EthType;
Ari Saha89831742015-06-26 10:31:48 -070032import org.onlab.packet.Ethernet;
33import org.onlab.packet.IPv4;
34import org.onlab.packet.Ip4Address;
35import org.onlab.packet.IpAddress;
36import org.onlab.packet.MacAddress;
Jonathan Harta46dddf2015-06-30 15:31:20 -070037import org.onlab.packet.RADIUS;
38import org.onlab.packet.RADIUSAttribute;
Hyunsun Moona0d63712015-08-22 21:04:23 -070039import org.onlab.packet.TpPort;
Ari Saha89831742015-06-26 10:31:48 -070040import org.onlab.packet.UDP;
41import org.onlab.packet.VlanId;
Ari Saha89831742015-06-26 10:31:48 -070042import 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;
Ray Milkeyfcb623d2015-10-01 16:48:18 -070048import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigEvent;
50import org.onosproject.net.config.NetworkConfigListener;
51import org.onosproject.net.config.NetworkConfigRegistry;
Ari Saha89831742015-06-26 10:31:48 -070052import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
Ari Saha89831742015-06-26 10:31:48 -070054import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.host.HostService;
Ari Saha89831742015-06-26 10:31:48 -070057import org.onosproject.net.packet.DefaultOutboundPacket;
58import org.onosproject.net.packet.InboundPacket;
59import org.onosproject.net.packet.OutboundPacket;
60import org.onosproject.net.packet.PacketContext;
Ari Saha89831742015-06-26 10:31:48 -070061import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
Ari Saha89831742015-06-26 10:31:48 -070063import org.onosproject.xosintegration.VoltTenantService;
Ari Saha89831742015-06-26 10:31:48 -070064import org.slf4j.Logger;
65
Ray Milkeyfcb623d2015-10-01 16:48:18 -070066import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -070067import static org.onosproject.net.packet.PacketPriority.CONTROL;
Ari Saha89831742015-06-26 10:31:48 -070068import static org.slf4j.LoggerFactory.getLogger;
69
Ari Saha89831742015-06-26 10:31:48 -070070/**
Jonathan Harta46dddf2015-06-30 15:31:20 -070071 * AAA application for ONOS.
Ari Saha89831742015-06-26 10:31:48 -070072 */
73@Component(immediate = true)
74public class AAA {
Ray Milkeyf51eba22015-09-25 10:24:23 -070075
Ray Milkeyf61a24e2015-09-24 16:34:02 -070076 // for verbose output
77 private final Logger log = getLogger(getClass());
Ray Milkeyf51eba22015-09-25 10:24:23 -070078
Ray Milkeyf61a24e2015-09-24 16:34:02 -070079 // a list of our dependencies :
80 // to register with ONOS as an application - described next
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected CoreService coreService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070083
Ray Milkeyf61a24e2015-09-24 16:34:02 -070084 // to receive Packet-in events that we'll respond to
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected PacketService packetService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070087
Ray Milkeyf61a24e2015-09-24 16:34:02 -070088 // end host information
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected HostService hostService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070091
Ray Milkeyf61a24e2015-09-24 16:34:02 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected VoltTenantService voltTenantService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070094
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkeyfcb623d2015-10-01 16:48:18 -070096 protected NetworkConfigRegistry netCfgService;
Ray Milkeyf51eba22015-09-25 10:24:23 -070097
Ray Milkeyfcb623d2015-10-01 16:48:18 -070098 // Parsed RADIUS server addresses
99 protected InetAddress radiusIpAddress;
100 protected String radiusMacAddress;
101
102 // NAS IP address
103 protected InetAddress nasIpAddress;
104 protected String nasMacAddress;
105
106 // RADIUS server secret
107 protected String radiusSecret;
108
109 // ID of RADIUS switch
110 protected String radiusSwitch;
111
112 // RADIUS port number
113 protected long radiusPort;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700114
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700115 // our application-specific event handler
116 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
Ray Milkeyf51eba22015-09-25 10:24:23 -0700117
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700118 // our unique identifier
119 private ApplicationId appId;
Ray Milkeyf51eba22015-09-25 10:24:23 -0700120
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700121 // Configuration properties factory
122 private final ConfigFactory factory =
123 new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY,
124 AAAConfig.class,
125 "AAA") {
126 @Override
127 public AAAConfig createConfig() {
128 return new AAAConfig();
129 }
130 };
Ray Milkeyf51eba22015-09-25 10:24:23 -0700131
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700132 // Listener for config changes
133 private final InternalConfigListener cfgListener = new InternalConfigListener();
Ari Saha89831742015-06-26 10:31:48 -0700134
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700135 /**
136 * Builds an EAPOL packet based on the given parameters.
137 *
138 * @param dstMac destination MAC address
139 * @param srcMac source MAC address
140 * @param vlan vlan identifier
141 * @param eapolType EAPOL type
142 * @param eap EAP payload
143 * @return Ethernet frame
144 */
145 private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
146 short vlan, byte eapolType, EAP eap) {
Ari Saha89831742015-06-26 10:31:48 -0700147
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700148 Ethernet eth = new Ethernet();
149 eth.setDestinationMACAddress(dstMac.toBytes());
150 eth.setSourceMACAddress(srcMac.toBytes());
151 eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
152 if (vlan != Ethernet.VLAN_UNTAGGED) {
153 eth.setVlanID(vlan);
154 }
155 //eapol header
156 EAPOL eapol = new EAPOL();
157 eapol.setEapolType(eapolType);
158 eapol.setPacketLength(eap.getLength());
Ari Saha89831742015-06-26 10:31:48 -0700159
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700160 //eap part
161 eapol.setPayload(eap);
162
163 eth.setPayload(eapol);
164 eth.setPad(true);
165 return eth;
166 }
Ari Saha89831742015-06-26 10:31:48 -0700167
Ari Saha89831742015-06-26 10:31:48 -0700168 @Activate
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700169 public void activate() {
170 netCfgService.addListener(cfgListener);
171 netCfgService.registerConfigFactory(factory);
172
Ari Saha89831742015-06-26 10:31:48 -0700173 // "org.onosproject.aaa" is the FQDN of our app
174 appId = coreService.registerApplication("org.onosproject.aaa");
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700175
176 cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AAAConfig.class));
177
Ari Saha89831742015-06-26 10:31:48 -0700178 // register our event handler
Brian O'Connord9c7da02015-07-29 17:49:24 -0700179 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700180 requestIntercepts();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700181
182 StateMachine.initializeMaps();
Ari Saha89831742015-06-26 10:31:48 -0700183
184 hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
Ari Saha89831742015-06-26 10:31:48 -0700185 }
186
187 @Deactivate
188 public void deactivate() {
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700189 appId = coreService.registerApplication("org.onosproject.aaa");
190 withdrawIntercepts();
Ari Saha89831742015-06-26 10:31:48 -0700191 // de-register and null our handler
192 packetService.removeProcessor(processor);
193 processor = null;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700194 StateMachine.destroyMaps();
Ari Saha89831742015-06-26 10:31:48 -0700195 }
196
Jonathan Harta46dddf2015-06-30 15:31:20 -0700197 /**
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700198 * Request packet in via PacketService.
199 */
200 private void requestIntercepts() {
201 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
202 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
203 packetService.requestPackets(selector.build(),
204 CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700205
206 TrafficSelector radSelector = DefaultTrafficSelector.builder()
207 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
208 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700209 .matchUdpDst(TpPort.tpPort(1812))
210 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700211 .build();
212 packetService.requestPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700213 }
214
215 /**
216 * Cancel request for packet in via PacketService.
217 */
218 private void withdrawIntercepts() {
219 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
220 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
221 packetService.cancelPackets(selector.build(), CONTROL, appId);
Jonathan Hartfeb41762015-07-15 15:10:28 -0700222
223 TrafficSelector radSelector = DefaultTrafficSelector.builder()
224 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
225 .matchIPProtocol(IPv4.PROTOCOL_UDP)
Hyunsun Moona0d63712015-08-22 21:04:23 -0700226 .matchUdpDst(TpPort.tpPort(1812))
227 .matchUdpSrc(TpPort.tpPort(1812))
Jonathan Hartfeb41762015-07-15 15:10:28 -0700228 .build();
229 packetService.cancelPackets(radSelector, CONTROL, appId);
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700230 }
231
Ari Saha89831742015-06-26 10:31:48 -0700232 // our handler defined as a private inner class
233
234 /**
235 * Packet processor responsible for forwarding packets along their paths.
236 */
237 private class ReactivePacketProcessor implements PacketProcessor {
238 @Override
239 public void process(PacketContext context) {
240
241 // Extract the original Ethernet frame from the packet information
242 InboundPacket pkt = context.inPacket();
243 Ethernet ethPkt = pkt.parsed();
244 if (ethPkt == null) {
245 return;
246 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700247 try {
248 // identify if incoming packet comes from supplicant (EAP) or RADIUS
249 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
250 case EAPOL:
251 handleSupplicantPacket(context.inPacket());
252 break;
253 case IPV4:
254 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
255 Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700256 Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700257 if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
258 // TODO: check for port as well when it's configurable
259 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Harta46dddf2015-06-30 15:31:20 -0700260
Ray Milkeyf51eba22015-09-25 10:24:23 -0700261 byte[] datagram = udpPacket.getPayload().serialize();
262 RADIUS radiusPacket;
Jonathan Harta46dddf2015-06-30 15:31:20 -0700263 radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700264 handleRadiusPacket(radiusPacket);
Jonathan Harta46dddf2015-06-30 15:31:20 -0700265 }
Ray Milkeyf51eba22015-09-25 10:24:23 -0700266
267 break;
268 default:
269 log.trace("Skipping Ethernet packet type {}",
270 EthType.EtherType.lookup(ethPkt.getEtherType()));
271 }
272 } catch (DeserializationException | StateMachineException e) {
273 log.warn("Unable to process RADIUS packet:", e);
Ari Saha89831742015-06-26 10:31:48 -0700274 }
275 }
276
Ray Milkey9eb293f2015-09-30 15:09:17 -0700277 /**
278 * Creates and initializes common fields of a RADIUS packet.
279 *
280 * @param identifier RADIUS identifier
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700281 * @param eapPacket EAP packet
Ray Milkey9eb293f2015-09-30 15:09:17 -0700282 * @return RADIUS packet
283 */
284 private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) {
285 RADIUS radiusPayload =
286 new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
287 eapPacket.getIdentifier());
288 radiusPayload.setIdentifier(identifier);
289 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
290 eapPacket.getData());
291
292 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700293 AAA.this.nasIpAddress.getAddress());
Ray Milkey9eb293f2015-09-30 15:09:17 -0700294
295 radiusPayload.encapsulateMessage(eapPacket);
296 radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
297
298 return radiusPayload;
299 }
Ari Saha89831742015-06-26 10:31:48 -0700300
301 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700302 * Handles PAE packets (supplicant).
303 *
304 * @param inPacket Ethernet packet coming from the supplicant
Ari Saha89831742015-06-26 10:31:48 -0700305 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700306 private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Jonathan Harta46dddf2015-06-30 15:31:20 -0700307 Ethernet ethPkt = inPacket.parsed();
Ari Saha89831742015-06-26 10:31:48 -0700308 // Where does it come from?
309 MacAddress srcMAC = ethPkt.getSourceMAC();
310
Jonathan Harta46dddf2015-06-30 15:31:20 -0700311 DeviceId deviceId = inPacket.receivedFrom().deviceId();
312 PortNumber portNumber = inPacket.receivedFrom().port();
Ari Saha89831742015-06-26 10:31:48 -0700313 String sessionId = deviceId.toString() + portNumber.toString();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700314 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
315 if (stateMachine == null) {
316 stateMachine = new StateMachine(sessionId, voltTenantService);
317 }
318
Jonathan Harta46dddf2015-06-30 15:31:20 -0700319
320 EAPOL eapol = (EAPOL) ethPkt.getPayload();
Ari Saha89831742015-06-26 10:31:48 -0700321
322 switch (eapol.getEapolType()) {
323 case EAPOL.EAPOL_START:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700324 stateMachine.start();
325 stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
Ari Saha89831742015-06-26 10:31:48 -0700326
Ray Milkeyf51eba22015-09-25 10:24:23 -0700327 //send an EAP Request/Identify to the supplicant
328 EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
329 Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
330 ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
331 eapPayload);
332 stateMachine.setSupplicantAddress(srcMAC);
333 stateMachine.setVlanId(ethPkt.getVlanID());
Ari Saha89831742015-06-26 10:31:48 -0700334
Ray Milkeyf51eba22015-09-25 10:24:23 -0700335 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700336
337 break;
338 case EAPOL.EAPOL_PACKET:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700339 RADIUS radiusPayload;
Ray Milkey9eb293f2015-09-30 15:09:17 -0700340 // check if this is a Response/Identify or a Response/TLS
Ari Saha89831742015-06-26 10:31:48 -0700341 EAP eapPacket = (EAP) eapol.getPayload();
342
343 byte dataType = eapPacket.getDataType();
344 switch (dataType) {
Ari Saha89831742015-06-26 10:31:48 -0700345
Ray Milkey9eb293f2015-09-30 15:09:17 -0700346 case EAP.ATTR_IDENTITY:
347 // request id access to RADIUS
348 stateMachine.setUsername(eapPacket.getData());
Ari Saha89831742015-06-26 10:31:48 -0700349
Ray Milkey9eb293f2015-09-30 15:09:17 -0700350 radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700351
Ray Milkey9eb293f2015-09-30 15:09:17 -0700352 // set Request Authenticator in StateMachine
353 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
354 sendRadiusMessage(radiusPayload);
Ray Milkeyf51eba22015-09-25 10:24:23 -0700355
Ray Milkey9eb293f2015-09-30 15:09:17 -0700356 // change the state to "PENDING"
357 stateMachine.requestAccess();
358 break;
Ari Saha89831742015-06-26 10:31:48 -0700359 case EAP.ATTR_MD5:
Ray Milkey9eb293f2015-09-30 15:09:17 -0700360 // verify if the EAP identifier corresponds to the
361 // challenge identifier from the client state
362 // machine.
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700363 if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
Ari Saha89831742015-06-26 10:31:48 -0700364 //send the RADIUS challenge response
Ray Milkey9eb293f2015-09-30 15:09:17 -0700365 radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700366
367 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700368 stateMachine.challengeState());
Ari Saha89831742015-06-26 10:31:48 -0700369 sendRadiusMessage(radiusPayload);
370 }
371 break;
372 case EAP.ATTR_TLS:
Ray Milkey9eb293f2015-09-30 15:09:17 -0700373 // request id access to RADIUS
374 radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
Ari Saha89831742015-06-26 10:31:48 -0700375
Ray Milkeyf51eba22015-09-25 10:24:23 -0700376 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
377 stateMachine.challengeState());
378 stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
Ari Saha89831742015-06-26 10:31:48 -0700379
Ray Milkeyf51eba22015-09-25 10:24:23 -0700380 sendRadiusMessage(radiusPayload);
381 // TODO: this gets called on every fragment, should only be called at TLS-Start
382 stateMachine.requestAccess();
383
Ari Saha89831742015-06-26 10:31:48 -0700384 break;
385 default:
386 return;
387 }
388 break;
389 default:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700390 log.trace("Skipping EAPOL message {}", eapol.getEapolType());
Ari Saha89831742015-06-26 10:31:48 -0700391 }
392 }
393
394 /**
Jonathan Harta46dddf2015-06-30 15:31:20 -0700395 * Handles RADIUS packets.
396 *
Ari Saha89831742015-06-26 10:31:48 -0700397 * @param radiusPacket RADIUS packet coming from the RADIUS server.
398 */
Ray Milkeyf51eba22015-09-25 10:24:23 -0700399 private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700400 StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
Ari Saha89831742015-06-26 10:31:48 -0700401 if (stateMachine == null) {
402 log.error("Invalid session identifier, exiting...");
403 return;
404 }
405
Ray Milkeyf51eba22015-09-25 10:24:23 -0700406 EAP eapPayload;
407 Ethernet eth;
Ari Saha89831742015-06-26 10:31:48 -0700408 switch (radiusPacket.getCode()) {
409 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
410 byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
411 eapPayload = radiusPacket.decapsulateMessage();
412 stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700413 eth = buildEapolResponse(stateMachine.supplicantAddress(),
414 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700415 eapPayload);
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700416 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700417 break;
418 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700419 //send an EAPOL - Success to the supplicant.
420 byte[] eapMessage =
421 radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
422 eapPayload = new EAP();
423 eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
424 eth = buildEapolResponse(stateMachine.supplicantAddress(),
425 MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
426 eapPayload);
427 this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
Ari Saha89831742015-06-26 10:31:48 -0700428
Ray Milkeyf51eba22015-09-25 10:24:23 -0700429 stateMachine.authorizeAccess();
Ari Saha89831742015-06-26 10:31:48 -0700430 break;
431 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
Ray Milkeyf51eba22015-09-25 10:24:23 -0700432 stateMachine.denyAccess();
Ari Saha89831742015-06-26 10:31:48 -0700433 break;
434 default:
435 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
436 }
437 }
438
Ari Saha89831742015-06-26 10:31:48 -0700439 private void sendRadiusMessage(RADIUS radiusMessage) {
440 Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
441 Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
442
443 Host dst;
444 if (!odst.isPresent()) {
445 log.info("Radius server {} is not present", radiusIpAddress);
446 return;
447 } else {
448 dst = odst.get();
449 }
450
451 UDP udp = new UDP();
452 IPv4 ip4Packet = new IPv4();
453 Ethernet ethPkt = new Ethernet();
454 radiusMessage.setParent(udp);
455 udp.setDestinationPort((short) 1812);
456 udp.setSourcePort((short) 1812); // TODO: make this configurable
457 udp.setPayload(radiusMessage);
458 udp.setParent(ip4Packet);
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700459 ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress());
460 ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress());
Ari Saha89831742015-06-26 10:31:48 -0700461 ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
462 ip4Packet.setPayload(udp);
463 ip4Packet.setParent(ethPkt);
464 ethPkt.setDestinationMACAddress(radiusMacAddress);
465 ethPkt.setSourceMACAddress(nasMacAddress);
466 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
467 ethPkt.setPayload(ip4Packet);
468
469 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700470 .setOutput(PortNumber.portNumber(radiusPort)).build();
Ari Saha89831742015-06-26 10:31:48 -0700471 OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700472 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700473 packetService.emit(packet);
474
475 }
476
Ari Saha89831742015-06-26 10:31:48 -0700477 /**
478 * Send the ethernet packet to the supplicant.
Jonathan Harta46dddf2015-06-30 15:31:20 -0700479 *
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700480 * @param ethernetPkt the ethernet packet
Jonathan Harta46dddf2015-06-30 15:31:20 -0700481 * @param connectPoint the connect point to send out
Ari Saha89831742015-06-26 10:31:48 -0700482 */
483 private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
484 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
485 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
Aaron Kruglikovd39d99e2015-07-03 13:30:57 -0700486 treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
Ari Saha89831742015-06-26 10:31:48 -0700487 packetService.emit(packet);
488 }
489
490 }
491
Ray Milkeyfcb623d2015-10-01 16:48:18 -0700492 private class InternalConfigListener implements NetworkConfigListener {
493
494 /**
495 * Reconfigures the DHCP Server according to the configuration parameters passed.
496 *
497 * @param cfg configuration object
498 */
499 private void reconfigureNetwork(AAAConfig cfg) {
500 AAAConfig newCfg;
501 if (cfg == null) {
502 newCfg = new AAAConfig();
503 } else {
504 newCfg = cfg;
505 }
506 if (newCfg.nasIp() != null) {
507 nasIpAddress = newCfg.nasIp();
508 }
509 if (newCfg.radiusIp() != null) {
510 radiusIpAddress = newCfg.radiusIp();
511 }
512 if (newCfg.radiusMac() != null) {
513 radiusMacAddress = newCfg.radiusMac();
514 }
515 if (newCfg.nasMac() != null) {
516 nasMacAddress = newCfg.nasMac();
517 }
518 if (newCfg.radiusSecret() != null) {
519 radiusSecret = newCfg.radiusSecret();
520 }
521 if (newCfg.radiusSwitch() != null) {
522 radiusSwitch = newCfg.radiusSwitch();
523 }
524 if (newCfg.radiusPort() != -1) {
525 radiusPort = newCfg.radiusPort();
526 }
527
528 log.info("AAA app configuration:");
529 log.info("NAS IP is {}", nasIpAddress);
530 log.info("RADIUS IP is {}", radiusIpAddress);
531 log.info("NAS MAC is {}", nasMacAddress);
532 log.info("RADIUS MAC is {}", radiusMacAddress);
533 log.info("RADIUS secret is {}", radiusSecret);
534 log.info("RADIUS switch is {}", radiusSwitch);
535 log.info("RADIUS port is {}", radiusPort);
536 }
537
538 @Override
539 public void event(NetworkConfigEvent event) {
540
541 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
542 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
543 event.configClass().equals(AAAConfig.class)) {
544
545 AAAConfig cfg = netCfgService.getConfig(appId, AAAConfig.class);
546 reconfigureNetwork(cfg);
547 log.info("Reconfigured");
548 }
549 }
550 }
551
552
Ari Saha89831742015-06-26 10:31:48 -0700553}