Ari Saha
Committed by Gerrit Code Review

ONOS AAA app: Authentication and Authorization logic.

Change-Id: I36eb889eeab38edf12377c13e780a147551459a4
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<app name="org.onosproject.aaa" origin="ATT" version="${project.version}"
18 + featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
19 + features="${project.artifactId}">
20 + <description>${project.description}</description>
21 + <artifact>mvn:${project.groupId}/${project.artifactId}/${project.version}</artifact>
22 + <artifact>mvn:${project.groupId}/onos-app-xos-integration/${project.version}</artifact>
23 + <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle>
24 +</app>
1 +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
18 + <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
19 + <feature name="${project.artifactId}" version="${project.version}"
20 + description="${project.description}">
21 + <feature>onos-api</feature>
22 + <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle>
23 + <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
24 + <bundle>mvn:${project.groupId}/onos-app-xos-integration/${project.version}</bundle>
25 + </feature>
26 +</features>
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 +
23 + <parent>
24 + <groupId>org.onosproject</groupId>
25 + <artifactId>onos-apps</artifactId>
26 + <version>1.3.0-SNAPSHOT</version>
27 + <relativePath>../pom.xml</relativePath>
28 + </parent>
29 +
30 + <artifactId>onos-app-aaa</artifactId>
31 + <packaging>bundle</packaging>
32 +
33 + <description>ONOS authentication application</description>
34 +
35 + <properties>
36 + <onos.app.name>org.onosproject.aaa</onos.app.name>
37 + </properties>
38 +
39 + <dependencies>
40 + <dependency>
41 + <groupId>org.osgi</groupId>
42 + <artifactId>org.osgi.compendium</artifactId>
43 + </dependency>
44 +
45 + <dependency>
46 + <groupId>org.onosproject</groupId>
47 + <artifactId>onlab-junit</artifactId>
48 + <scope>test</scope>
49 + </dependency>
50 +
51 + <dependency>
52 + <groupId>org.onosproject</groupId>
53 + <artifactId>onos-api</artifactId>
54 + <version>${project.version}</version>
55 + </dependency>
56 +
57 + <dependency>
58 + <groupId>org.onosproject</groupId>
59 + <artifactId>onlab-osgi</artifactId>
60 + <version>${project.version}</version>
61 + </dependency>
62 +
63 + <dependency>
64 + <groupId>org.apache.felix</groupId>
65 + <artifactId>org.apache.felix.scr.annotations</artifactId>
66 + </dependency>
67 +
68 + <dependency>
69 + <groupId>org.onosproject</groupId>
70 + <artifactId>onos-app-xos-integration</artifactId>
71 + <version>${project.version}</version>
72 + </dependency>
73 + </dependencies>
74 +
75 +
76 + <build>
77 + <plugins>
78 + <plugin>
79 + <groupId>org.apache.felix</groupId>
80 + <artifactId>maven-bundle-plugin</artifactId>
81 + </plugin>
82 +
83 + <plugin>
84 + <groupId>org.apache.felix</groupId>
85 + <artifactId>maven-scr-plugin</artifactId>
86 + </plugin>
87 + <plugin>
88 + <groupId>org.onosproject</groupId>
89 + <artifactId>onos-maven-plugin</artifactId>
90 + </plugin>
91 + </plugins>
92 + </build>
93 +</project>
1 +/*
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 + */
16 +package org.onosproject.aaa;
17 +
18 +import com.google.common.base.Strings;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Modified;
23 +import org.apache.felix.scr.annotations.Property;
24 +import org.apache.felix.scr.annotations.Reference;
25 +import org.apache.felix.scr.annotations.ReferenceCardinality;
26 +import org.onlab.packet.Ethernet;
27 +import org.onlab.packet.IPv4;
28 +import org.onlab.packet.Ip4Address;
29 +import org.onlab.packet.IpAddress;
30 +import org.onlab.packet.MacAddress;
31 +import org.onlab.packet.UDP;
32 +import org.onlab.packet.VlanId;
33 +import org.onlab.util.Tools;
34 +import org.onosproject.aaa.packet.EAP;
35 +import org.onosproject.aaa.packet.EAPEthernet;
36 +import org.onosproject.aaa.packet.EAPOL;
37 +import org.onosproject.aaa.packet.RADIUS;
38 +import org.onosproject.aaa.packet.RADIUSAttribute;
39 +import org.onosproject.cfg.ComponentConfigService;
40 +import org.onosproject.core.ApplicationId;
41 +import org.onosproject.core.CoreService;
42 +import org.onosproject.net.ConnectPoint;
43 +import org.onosproject.net.DeviceId;
44 +import org.onosproject.net.Host;
45 +import org.onosproject.net.PortNumber;
46 +import org.onosproject.net.flow.DefaultTrafficSelector;
47 +import org.onosproject.net.flow.DefaultTrafficTreatment;
48 +import org.onosproject.net.flow.FlowRuleService;
49 +import org.onosproject.net.flow.TrafficSelector;
50 +import org.onosproject.net.flow.TrafficTreatment;
51 +import org.onosproject.net.host.HostService;
52 +import org.onosproject.net.intent.IntentService;
53 +import org.onosproject.net.packet.DefaultOutboundPacket;
54 +import org.onosproject.net.packet.InboundPacket;
55 +import org.onosproject.net.packet.OutboundPacket;
56 +import org.onosproject.net.packet.PacketContext;
57 +import org.onosproject.net.packet.PacketPriority;
58 +import org.onosproject.net.packet.PacketProcessor;
59 +import org.onosproject.net.packet.PacketService;
60 +import org.onosproject.net.topology.TopologyService;
61 +import org.onosproject.xosintegration.VoltTenantService;
62 +import org.osgi.service.component.ComponentContext;
63 +import org.slf4j.Logger;
64 +
65 +import java.net.InetAddress;
66 +import java.net.UnknownHostException;
67 +import java.nio.ByteBuffer;
68 +import java.util.Collections;
69 +import java.util.Dictionary;
70 +import java.util.HashMap;
71 +import java.util.Iterator;
72 +import java.util.Map;
73 +import java.util.Optional;
74 +import java.util.Set;
75 +
76 +import static org.slf4j.LoggerFactory.getLogger;
77 +
78 +
79 +/**
80 + * AAA application for Onos.
81 + */
82 +@Component(immediate = true)
83 +public class AAA {
84 + // a list of our dependencies :
85 + // to register with ONOS as an application - described next
86 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 + protected CoreService coreService;
88 +
89 + // topology information
90 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 + protected TopologyService topologyService;
92 +
93 + // to receive Packet-in events that we'll respond to
94 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 + protected PacketService packetService;
96 +
97 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 + protected FlowRuleService flowService;
99 +
100 + // to submit/withdraw intents for traffic manipulation
101 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 + protected IntentService intentService;
103 +
104 + // end host information
105 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 + protected HostService hostService;
107 +
108 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 + protected VoltTenantService voltTenantService;
110 +
111 +
112 + // for verbose output
113 + private final Logger log = getLogger(getClass());
114 +
115 + // our application-specific event handler
116 + private ReactivePacketProcessor processor = new ReactivePacketProcessor();
117 +
118 + // our unique identifier
119 + private ApplicationId appId;
120 +
121 + // Map of state machines. Each state machine is represented by an
122 + // unique identifier on the switch: dpid + port number
123 + Map stateMachineMap = null;
124 +
125 + // RADIUS server IP address
126 + private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
127 + // NAS IP address
128 + private static final String DEFAULT_NAS_IP = "192.168.1.11";
129 + // RADIUS uplink port
130 + private static final int DEFAULT_RADIUS_UPLINK = 2;
131 + // RADIUS server shared secret
132 + private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
133 + //RADIUS MAC address
134 + private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
135 + //NAS MAC address
136 + private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
137 + //Radius Switch Id
138 + private static final String DEFAULT_RADIUS_SWITCH = "of:5e3e486e73000187";
139 + //Radius Port Number
140 + private static final String DEFAULT_RADIUS_PORT = "5";
141 +
142 + @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
143 + label = "RADIUS IP Address")
144 + private String radiusIpAddress = DEFAULT_RADIUS_IP;
145 +
146 + @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
147 + label = "NAP IP Address")
148 + private String nasIpAddress = DEFAULT_NAS_IP;
149 +
150 + @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
151 + label = "RADIUS MAC Address")
152 + private String radiusMacAddress = RADIUS_MAC_ADDRESS;
153 +
154 + @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
155 + label = "NAP MAC Address")
156 + private String nasMacAddress = NAS_MAC_ADDRESS;
157 +
158 + @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
159 + label = "RADIUS shared secret")
160 + private String radiusSecret = DEFAULT_RADIUS_SECRET;
161 +
162 + @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
163 + label = "Radius switch")
164 + private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
165 +
166 + @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
167 + label = "Radius port")
168 + private String radiusPort = DEFAULT_RADIUS_PORT;
169 +
170 + // Parsed RADIUS server IP address
171 + protected InetAddress parsedRadiusIpAddress;
172 +
173 + // Parsed NAS IP address
174 + protected InetAddress parsedNasIpAddress;
175 +
176 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
177 + protected ComponentConfigService cfgService;
178 +
179 + @Modified
180 + public void modified(ComponentContext context) {
181 + Dictionary<?, ?> properties = context.getProperties();
182 +
183 + String s = Tools.get(properties, "radiusIpAddress");
184 + try {
185 + parsedRadiusIpAddress = InetAddress.getByName(s);
186 + radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
187 + } catch (UnknownHostException e) {
188 + log.error("Invalid RADIUS IP address specification: {}", s);
189 + }
190 + try {
191 + s = Tools.get(properties, "nasIpAddress");
192 + parsedNasIpAddress = InetAddress.getByName(s);
193 + nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
194 + } catch (UnknownHostException e) {
195 + log.error("Invalid NAS IP address specification: {}", s);
196 + }
197 +
198 + s = Tools.get(properties, "radiusMacAddress");
199 + radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
200 +
201 + s = Tools.get(properties, "nasMacAddress");
202 + nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
203 +
204 + s = Tools.get(properties, "radiusSecret");
205 + radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
206 +
207 + s = Tools.get(properties, "radiusSwitchId");
208 + radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
209 +
210 + s = Tools.get(properties, "radiusPortNumber");
211 + radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
212 + }
213 +
214 + @Activate
215 + public void activate(ComponentContext context) {
216 + cfgService.registerProperties(getClass());
217 + modified(context);
218 + // "org.onosproject.aaa" is the FQDN of our app
219 + appId = coreService.registerApplication("org.onosproject.aaa");
220 + // register our event handler
221 + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
222 + TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
223 +
224 + selector.matchEthType(EAPEthernet.TYPE_PAE);
225 + packetService.requestPackets(selector.build(),
226 + PacketPriority.CONTROL, appId);
227 +
228 + // Instantiate the map of the state machines
229 + Map<String, StateMachine> stateMachines = new HashMap<String, StateMachine>();
230 + stateMachineMap = Collections.synchronizedMap(stateMachines);
231 +
232 + hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
233 +
234 + }
235 +
236 + @Deactivate
237 + public void deactivate() {
238 + cfgService.unregisterProperties(getClass(), false);
239 + // de-register and null our handler
240 + packetService.removeProcessor(processor);
241 + processor = null;
242 + }
243 +
244 + // our handler defined as a private inner class
245 +
246 + /**
247 + * Packet processor responsible for forwarding packets along their paths.
248 + */
249 + private class ReactivePacketProcessor implements PacketProcessor {
250 + @Override
251 + public void process(PacketContext context) {
252 +
253 + // Extract the original Ethernet frame from the packet information
254 + InboundPacket pkt = context.inPacket();
255 + Ethernet ethPkt = pkt.parsed();
256 + if (ethPkt == null) {
257 + return;
258 + }
259 + //identify if incoming packet comes from supplicant (EAP) or RADIUS
260 + switch (ethPkt.getEtherType()) {
261 + case (short) 0x888e:
262 + handleSupplicantPacket(ethPkt, context);
263 + break;
264 + case 0x800:
265 + IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
266 + Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
267 + Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
268 + if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
269 + // TODO: check for port as well when it's configurable
270 + UDP udpPacket = (UDP) ipv4Packet.getPayload();
271 + // RADIUS radiusPacket = (RADIUS) udpPacket.getPayload();
272 + byte[] datagram = udpPacket.getPayload().serialize();
273 + RADIUS radiusPacket = new RADIUS();
274 + radiusPacket = (RADIUS) radiusPacket.deserialize(datagram, 0, datagram.length);
275 + handleRadiusPacket(radiusPacket);
276 + }
277 + break;
278 + default:
279 + return;
280 + }
281 + }
282 +
283 +
284 + /**
285 + * Handle PAE packets (supplicant).
286 + * @param ethPkt Ethernet packet coming from the supplicant.
287 + */
288 + private void handleSupplicantPacket(Ethernet ethPkt, PacketContext context) {
289 + // Where does it come from?
290 + MacAddress srcMAC = ethPkt.getSourceMAC();
291 +
292 + DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
293 + PortNumber portNumber = context.inPacket().receivedFrom().port();
294 + String sessionId = deviceId.toString() + portNumber.toString();
295 + StateMachine stateMachine = getStateMachine(sessionId);
296 + //Reserialize the data of the eth packet into our EAPOL format
297 + // this code will go once it is in the onos repository.
298 + byte[] bullshit = ethPkt.getPayload().serialize();
299 + EAPOL eapol = (EAPOL) new EAPOL().deserialize(bullshit, 0, bullshit.length);
300 +
301 + switch (eapol.getEapolType()) {
302 + case EAPOL.EAPOL_START:
303 + try {
304 + stateMachine.start();
305 + stateMachine.supplicantConnectpoint = context.inPacket().receivedFrom();
306 +
307 + //send an EAP Request/Identify to the supplicant
308 + EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
309 + Ethernet eth = EAPOL.buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
310 + ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
311 + eapPayload);
312 + stateMachine.supplicantAddress = srcMAC;
313 + stateMachine.vlanId = ethPkt.getVlanID();
314 +
315 + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
316 + } catch (StateMachineException e) {
317 + e.printStackTrace();
318 + }
319 +
320 + break;
321 + case EAPOL.EAPOL_PACKET:
322 + //check if this is a Response/Idenfity or a Response/TLS
323 + EAP eapPacket = (EAP) eapol.getPayload();
324 +
325 + byte dataType = eapPacket.getDataType();
326 + switch (dataType) {
327 + case EAP.ATTR_IDENTITY:
328 + try {
329 + //request id access to RADIUS
330 + RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
331 + eapPacket.getIdentifier());
332 + radiusPayload.setIdentifier(stateMachine.getIdentifier());
333 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
334 + eapPacket.getData());
335 + stateMachine.setUsername(eapPacket.getData());
336 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
337 + AAA.this.parsedNasIpAddress.getAddress());
338 +
339 + radiusPayload.encapsulateMessage(eapPacket);
340 +
341 + // set Request Authenticator in StateMachine
342 + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
343 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
344 + sendRadiusMessage(radiusPayload);
345 +
346 + //change the state to "PENDING"
347 + stateMachine.requestAccess();
348 + } catch (StateMachineException e) {
349 + e.printStackTrace();
350 + }
351 + break;
352 + case EAP.ATTR_MD5:
353 + //verify if the EAP identifier corresponds to the challenge identifier from the client state
354 + //machine.
355 + if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
356 + //send the RADIUS challenge response
357 + RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
358 + eapPacket.getIdentifier());
359 + radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
360 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
361 + stateMachine.getUsername());
362 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
363 + AAA.this.parsedNasIpAddress.getAddress());
364 +
365 + radiusPayload.encapsulateMessage(eapPacket);
366 +
367 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
368 + stateMachine.getChallengeState());
369 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
370 + sendRadiusMessage(radiusPayload);
371 + }
372 + break;
373 + case EAP.ATTR_TLS:
374 + try {
375 + //request id access to RADIUS
376 + RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
377 + eapPacket.getIdentifier());
378 + radiusPayload.setIdentifier(stateMachine.getIdentifier());
379 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
380 + stateMachine.getUsername());
381 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
382 + AAA.this.parsedNasIpAddress.getAddress());
383 +
384 + radiusPayload.encapsulateMessage(eapPacket);
385 +
386 + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
387 + stateMachine.getChallengeState());
388 + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
389 +
390 + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
391 +
392 + sendRadiusMessage(radiusPayload);
393 + // TODO: this gets called on every fragment, should only be called at TLS-Start
394 + stateMachine.requestAccess();
395 + } catch (StateMachineException e) {
396 + e.printStackTrace();
397 + }
398 + break;
399 + default:
400 + return;
401 + }
402 + break;
403 + default:
404 + return;
405 + }
406 + }
407 +
408 + /**
409 + * Handle RADIUS packets.
410 + * @param radiusPacket RADIUS packet coming from the RADIUS server.
411 + */
412 + private void handleRadiusPacket(RADIUS radiusPacket) {
413 + StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
414 + if (stateMachine == null) {
415 + log.error("Invalid session identifier, exiting...");
416 + return;
417 + }
418 +
419 + byte[] eapMessage = null;
420 + EAP eapPayload = new EAP();
421 + Ethernet eth = null;
422 + switch (radiusPacket.getCode()) {
423 + case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
424 + byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
425 + eapPayload = radiusPacket.decapsulateMessage();
426 + stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
427 + eth = EAPOL.buildEapolResponse(stateMachine.supplicantAddress,
428 + MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, eapPayload);
429 + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
430 + break;
431 + case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
432 + try {
433 + //send an EAPOL - Success to the supplicant.
434 + eapMessage =
435 + radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
436 + eapPayload = new EAP();
437 + eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
438 + eth = EAPOL.buildEapolResponse(stateMachine.supplicantAddress,
439 + MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, eapPayload);
440 + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
441 +
442 + stateMachine.authorizeAccess();
443 + } catch (StateMachineException e) {
444 + e.printStackTrace();
445 + }
446 + break;
447 + case RADIUS.RADIUS_CODE_ACCESS_REJECT:
448 + try {
449 + stateMachine.denyAccess();
450 + } catch (StateMachineException e) {
451 + e.printStackTrace();
452 + }
453 + break;
454 + default:
455 + log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
456 + }
457 + }
458 +
459 + private StateMachine getStateMachineById(byte identifier) {
460 + StateMachine stateMachine = null;
461 + Set stateMachineSet = stateMachineMap.entrySet();
462 +
463 + synchronized (stateMachineMap) {
464 + Iterator itr = stateMachineSet.iterator();
465 + while (itr.hasNext()) {
466 + Map.Entry entry = (Map.Entry) itr.next();
467 + stateMachine = (StateMachine) entry.getValue();
468 + if (identifier == stateMachine.getIdentifier()) {
469 + //the state machine has already been created for this session session
470 + stateMachine = (StateMachine) entry.getValue();
471 + break;
472 + }
473 + }
474 + }
475 +
476 + return stateMachine;
477 + }
478 +
479 + private StateMachine getStateMachine(String sessionId) {
480 + StateMachine stateMachine = null;
481 + Set stateMachineSet = stateMachineMap.entrySet();
482 +
483 + synchronized (stateMachineMap) {
484 + Iterator itr = stateMachineSet.iterator();
485 + while (itr.hasNext()) {
486 +
487 + Map.Entry entry = (Map.Entry) itr.next();
488 + if (sessionId.equals(entry.getKey())) {
489 + //the state machine has already been created for this session session
490 + stateMachine = (StateMachine) entry.getValue();
491 + break;
492 + }
493 + }
494 + }
495 +
496 + if (stateMachine == null) {
497 + stateMachine = new StateMachine(sessionId, voltTenantService);
498 + stateMachineMap.put(sessionId, stateMachine);
499 + }
500 +
501 + return stateMachine;
502 + }
503 +
504 + private void sendRadiusMessage(RADIUS radiusMessage) {
505 + Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
506 + Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
507 +
508 + Host dst;
509 + if (!odst.isPresent()) {
510 + log.info("Radius server {} is not present", radiusIpAddress);
511 + return;
512 + } else {
513 + dst = odst.get();
514 + }
515 +
516 + UDP udp = new UDP();
517 + IPv4 ip4Packet = new IPv4();
518 + Ethernet ethPkt = new Ethernet();
519 + radiusMessage.setParent(udp);
520 + udp.setDestinationPort((short) 1812);
521 + udp.setSourcePort((short) 1812); // TODO: make this configurable
522 + udp.setPayload(radiusMessage);
523 + udp.setParent(ip4Packet);
524 + ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
525 + ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
526 + ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
527 + ip4Packet.setPayload(udp);
528 + ip4Packet.setParent(ethPkt);
529 + ethPkt.setDestinationMACAddress(radiusMacAddress);
530 + ethPkt.setSourceMACAddress(nasMacAddress);
531 + ethPkt.setEtherType(Ethernet.TYPE_IPV4);
532 + ethPkt.setPayload(ip4Packet);
533 +
534 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
535 + .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
536 + OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
537 + treatment, ByteBuffer.wrap(ethPkt.serialize()));
538 + packetService.emit(packet);
539 +
540 + }
541 +
542 +
543 + /**
544 + * Send the ethernet packet to the supplicant.
545 + * @param ethernetPkt the ethernet packet
546 + */
547 + private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
548 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
549 + OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
550 + treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
551 + packetService.emit(packet);
552 + }
553 +
554 + }
555 +
556 +}
1 +/*
2 + *
3 + * Copyright 2015 AT&T Foundry
4 + *
5 + * Licensed under the Apache License, Version 2.0 (the "License");
6 + * you may not use this file except in compliance with the License.
7 + * You may obtain a copy of the License at
8 + *
9 + * http://www.apache.org/licenses/LICENSE-2.0
10 + *
11 + * Unless required by applicable law or agreed to in writing, software
12 + * distributed under the License is distributed on an "AS IS" BASIS,
13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * See the License for the specific language governing permissions and
15 + * limitations under the License.
16 + *
17 + */
18 +
19 +package org.onosproject.aaa;
20 +
21 +import org.onlab.packet.MacAddress;
22 +import org.onosproject.net.ConnectPoint;
23 +import org.onosproject.xosintegration.VoltTenant;
24 +import org.onosproject.xosintegration.VoltTenantService;
25 +import org.slf4j.Logger;
26 +import java.util.BitSet;
27 +
28 +import static org.slf4j.LoggerFactory.getLogger;
29 +
30 +/**
31 + * AAA Finite State Machine.
32 + */
33 +
34 +class StateMachine {
35 + //INDEX to identify the state in the transition table
36 + static final int STATE_IDLE = 0;
37 + static final int STATE_STARTED = 1;
38 + static final int STATE_PENDING = 2;
39 + static final int STATE_AUTHORIZED = 3;
40 + static final int STATE_UNAUTHORIZED = 4;
41 +
42 + //INDEX to identify the transition in the transition table
43 + static final int TRANSITION_START = 0; // --> started
44 + static final int TRANSITION_REQUEST_ACCESS = 1;
45 + static final int TRANSITION_AUTHORIZE_ACCESS = 2;
46 + static final int TRANSITION_DENY_ACCESS = 3;
47 + static final int TRANSITION_LOGOFF = 4;
48 +
49 + //map of access identifiers (issued at EAPOL START)
50 + static BitSet bitSet = new BitSet();
51 + private final VoltTenantService voltService;
52 +
53 + private int identifier = -1;
54 + private byte challengeIdentifier;
55 + private byte[] challengeState;
56 + private byte[] username;
57 + private byte[] requestAuthenticator;
58 +
59 + // Supplicant connectivity info
60 + protected ConnectPoint supplicantConnectpoint;
61 + protected MacAddress supplicantAddress;
62 + protected short vlanId;
63 +
64 + private String sessionId = null;
65 +
66 + private final Logger log = getLogger(getClass());
67 +
68 +
69 + private State[] states = {
70 + new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
71 + };
72 +
73 +
74 + //State transition table
75 + /*
76 +
77 + state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED
78 + ////
79 + input
80 + ----------------------------------------------------------------------------------------------------
81 +
82 + START STARTED | _ | _ | _ | _
83 +
84 + REQUEST_ACCESS _ | PENDING | _ | _ | _
85 +
86 + AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
87 +
88 + DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
89 +
90 + LOGOFF _ | _ | _ | IDLE | IDLE
91 + */
92 +
93 + private int[] idleTransition =
94 + {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
95 + private int[] startedTransition =
96 + {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
97 + private int[] pendingTransition =
98 + {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
99 + private int[] authorizedTransition =
100 + {STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
101 + private int[] unauthorizedTransition =
102 + {STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
103 +
104 + //THE TRANSITION TABLE
105 + private int[][] transition =
106 + {idleTransition, startedTransition, pendingTransition, authorizedTransition,
107 + unauthorizedTransition};
108 +
109 + private int currentState = STATE_IDLE;
110 +
111 +
112 + /**
113 + * State Machine Constructor.
114 + * @param sessionId Session Id represented by the switch dpid + port number
115 + */
116 + public StateMachine(String sessionId, VoltTenantService voltService) {
117 + log.info("Creating a new state machine for {}", sessionId);
118 + this.sessionId = sessionId;
119 + this.voltService = voltService;
120 +
121 + }
122 +
123 + /**
124 + * Get the client id that is requesting for access.
125 + * @return The client id.
126 + */
127 + public String getSessionId() {
128 + return this.sessionId;
129 + }
130 +
131 + /**
132 + * Create the identifier for the state machine (happens when goes to STARTED state).
133 + */
134 + private void createIdentifier() throws StateMachineException {
135 + log.debug("Creating Identifier.");
136 + int index = -1;
137 +
138 + try {
139 + //find the first available spot for identifier assignment
140 + index = StateMachine.bitSet.nextClearBit(0);
141 +
142 + //there is a limit of 256 identifiers
143 + if (index == 256) {
144 + throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
145 + }
146 + } catch (IndexOutOfBoundsException e) {
147 + throw new StateMachineException(e.getMessage());
148 + }
149 +
150 + log.info("Assigning identifier {}", index);
151 + StateMachine.bitSet.set(index);
152 + this.identifier = index;
153 + }
154 +
155 + /**
156 + * Set the challenge identifier and the state issued by the RADIUS.
157 + * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
158 + * @param challengeState The challenge state from the RADIUS.
159 + */
160 + protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
161 + this.challengeIdentifier = challengeIdentifier;
162 + this.challengeState = challengeState;
163 + }
164 + /**
165 + * Set the challenge identifier issued by the RADIUS on the access challenge request.
166 + * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
167 + */
168 + protected void setChallengeIdentifier(byte challengeIdentifier) {
169 + log.info("Set Challenge Identifier to {}", challengeIdentifier);
170 + this.challengeIdentifier = challengeIdentifier;
171 + }
172 +
173 + /**
174 + * Get the challenge EAP identifier set by the RADIUS.
175 + * @return The challenge EAP identifier.
176 + */
177 + protected byte getChallengeIdentifier() {
178 + return this.challengeIdentifier;
179 + }
180 +
181 +
182 + /**
183 + * Set the challenge state info issued by the RADIUS.
184 + * @param challengeState The challenge state from the RADIUS.
185 + */
186 + protected void setChallengeState(byte[] challengeState) {
187 + log.info("Set Challenge State");
188 + this.challengeState = challengeState;
189 + }
190 +
191 + /**
192 + * Get the challenge state set by the RADIUS.
193 + * @return The challenge state.
194 + */
195 + protected byte[] getChallengeState() {
196 + return this.challengeState;
197 + }
198 +
199 + /**
200 + * Set the username.
201 + * @param username The username sent to the RADIUS upon access request.
202 + */
203 + protected void setUsername(byte[] username) {
204 + this.username = username;
205 + }
206 +
207 +
208 + /**
209 + * Get the username.
210 + * @return The requestAuthenticator.
211 + */
212 + protected byte[] getReqeustAuthenticator() {
213 + return this.requestAuthenticator;
214 + }
215 +
216 + /**
217 + * Set the username.
218 + * @param authenticator The username sent to the RADIUS upon access request.
219 + */
220 + protected void setRequestAuthenticator(byte[] authenticator) {
221 + this.requestAuthenticator = authenticator;
222 + }
223 +
224 +
225 + /**
226 + * Get the username.
227 + * @return The username.
228 + */
229 + protected byte[] getUsername() {
230 + return this.username;
231 + }
232 +
233 + /**
234 + * Return the identifier of the state machine.
235 + * @return The state machine identifier.
236 + */
237 + public byte getIdentifier() {
238 + return (byte) this.identifier;
239 + }
240 +
241 +
242 + protected void deleteIdentifier() {
243 + if (this.identifier != -1) {
244 + log.info("Freeing up " + this.identifier);
245 + //this state machine should be deleted and free up the identifier
246 + StateMachine.bitSet.clear(this.identifier);
247 + this.identifier = -1;
248 + }
249 + }
250 +
251 +
252 + /**
253 + * Move to the next state.
254 + * @param msg
255 + */
256 + private void next(int msg) {
257 + currentState = transition[currentState][msg];
258 + log.info("Current State " + currentState);
259 + }
260 +
261 + /**
262 + * Client has requested the start action to allow network access.
263 + */
264 + public void start() throws StateMachineException {
265 + try {
266 + states[currentState].start();
267 + //move to the next state
268 + next(TRANSITION_START);
269 + createIdentifier();
270 + } catch (StateMachineInvalidTransitionException e) {
271 + e.printStackTrace();
272 + }
273 + }
274 +
275 + /**
276 + * An Identification information has been sent by the supplicant.
277 + * Move to the next state if possible.
278 + */
279 + public void requestAccess() throws StateMachineException {
280 + try {
281 + states[currentState].requestAccess();
282 + //move to the next state
283 + next(TRANSITION_REQUEST_ACCESS);
284 + } catch (StateMachineInvalidTransitionException e) {
285 + e.printStackTrace();
286 + }
287 + }
288 +
289 + /**
290 + * RADIUS has accepted the identification.
291 + * Move to the next state if possible.
292 + */
293 + public void authorizeAccess() throws StateMachineException {
294 + try {
295 + states[currentState].radiusAccepted();
296 + //move to the next state
297 + next(TRANSITION_AUTHORIZE_ACCESS);
298 +
299 + if (voltService != null) {
300 + voltService.addTenant(
301 + VoltTenant.builder()
302 + .withHumanReadableName("VCPE-" + this.identifier)
303 + .withId(this.identifier)
304 + .withProviderService(1)
305 + .withServiceSpecificId(String.valueOf(this.identifier))
306 + .withPort(this.supplicantConnectpoint)
307 + .withVlanId(String.valueOf(this.vlanId)).build());
308 + }
309 +
310 + deleteIdentifier();
311 + } catch (StateMachineInvalidTransitionException e) {
312 + e.printStackTrace();
313 + }
314 +
315 + }
316 +
317 + /**
318 + * RADIUS has denied the identification.
319 + * Move to the next state if possible.
320 + */
321 + public void denyAccess() throws StateMachineException {
322 + try {
323 + states[currentState].radiusDenied();
324 + //move to the next state
325 + next(TRANSITION_DENY_ACCESS);
326 + deleteIdentifier();
327 + } catch (StateMachineInvalidTransitionException e) {
328 + e.printStackTrace();
329 + }
330 + }
331 +
332 + /**
333 + * Logoff request has been requested.
334 + * Move to the next state if possible.
335 + */
336 + public void logoff() throws StateMachineException {
337 + try {
338 + states[currentState].logoff();
339 + //move to the next state
340 + next(TRANSITION_LOGOFF);
341 + } catch (StateMachineInvalidTransitionException e) {
342 + e.printStackTrace();
343 + }
344 + }
345 +
346 + /**
347 + * Get the current state.
348 + * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
349 + * STATE_UNAUTHORIZED.
350 + */
351 + public int getState() {
352 + return currentState;
353 + }
354 +
355 +
356 +
357 + public String toString() {
358 + return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
359 + ("state: " + this.currentState);
360 + }
361 +}
362 +
363 +abstract class State {
364 + private final Logger log = getLogger(getClass());
365 +
366 + private String name = "State";
367 +
368 + public void start() throws StateMachineInvalidTransitionException {
369 + log.warn("START transition from this state is not allowed.");
370 + }
371 + public void requestAccess() throws StateMachineInvalidTransitionException {
372 + log.warn("REQUEST ACCESS transition from this state is not allowed.");
373 + }
374 + public void radiusAccepted() throws StateMachineInvalidTransitionException {
375 + log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
376 + }
377 + public void radiusDenied() throws StateMachineInvalidTransitionException {
378 + log.warn("DENY ACCESS transition from this state is not allowed.");
379 + }
380 + public void logoff() throws StateMachineInvalidTransitionException {
381 + log.warn("LOGOFF transition from this state is not allowed.");
382 + }
383 +}
384 +
385 +/**
386 + * Idle state: supplicant is logged of from the network.
387 + */
388 +class Idle extends State {
389 + private final Logger log = getLogger(getClass());
390 + private String name = "IDLE_STATE";
391 +
392 + public void start() {
393 + log.info("Moving from IDLE state to STARTED state.");
394 + }
395 +}
396 +
397 +/**
398 + * Started state: supplicant has entered the network and informed the authenticator.
399 + */
400 +class Started extends State {
401 + private final Logger log = getLogger(getClass());
402 + private String name = "STARTED_STATE";
403 +
404 + public void requestAccess() {
405 + log.info("Moving from STARTED state to PENDING state.");
406 + }
407 +}
408 +
409 +/**
410 + * Pending state: supplicant has been identified by the authenticator but has not access yet.
411 + */
412 +class Pending extends State {
413 + private final Logger log = getLogger(getClass());
414 + private String name = "PENDING_STATE";
415 +
416 + public void radiusAccepted() {
417 + log.info("Moving from PENDING state to AUTHORIZED state.");
418 + }
419 +
420 + public void radiusDenied() {
421 + log.info("Moving from PENDING state to UNAUTHORIZED state.");
422 + }
423 +}
424 +
425 +/**
426 + * Authorized state: supplicant port has been accepted, access is granted.
427 + */
428 +class Authorized extends State {
429 + private final Logger log = getLogger(getClass());
430 + private String name = "AUTHORIZED_STATE";
431 +
432 + public void logoff() {
433 +
434 + log.info("Moving from AUTHORIZED state to IDLE state.");
435 + }
436 +}
437 +
438 +/**
439 + * Unauthorized state: supplicant port has been rejected, access is denied.
440 + */
441 +class Unauthorized extends State {
442 + private final Logger log = getLogger(getClass());
443 + private String name = "UNAUTHORIZED_STATE";
444 +
445 + public void logoff() {
446 + log.info("Moving from UNAUTHORIZED state to IDLE state.");
447 + }
448 +}
449 +
450 +
451 +/**
452 + * Exception for the State Machine.
453 + */
454 +class StateMachineException extends Exception {
455 + public StateMachineException(String message) {
456 + super(message);
457 +
458 + }
459 +}
460 +/**
461 + * Exception raised when the transition from one state to another is invalid.
462 + */
463 +class StateMachineInvalidTransitionException extends StateMachineException {
464 + public StateMachineInvalidTransitionException(String message) {
465 + super(message);
466 + }
467 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
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 + */
16 +
17 +/**
18 + * AAA implmentation.
19 + */
20 +package org.onosproject.aaa;
1 +/*
2 + *
3 + * * Copyright 2015 AT&T Foundry
4 + * *
5 + * * Licensed under the Apache License, Version 2.0 (the "License");
6 + * * you may not use this file except in compliance with the License.
7 + * * You may obtain a copy of the License at
8 + * *
9 + * * http://www.apache.org/licenses/LICENSE-2.0
10 + * *
11 + * * Unless required by applicable law or agreed to in writing, software
12 + * * distributed under the License is distributed on an "AS IS" BASIS,
13 + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * * See the License for the specific language governing permissions and
15 + * * limitations under the License.
16 + *
17 + */
18 +
19 +package org.onosproject.aaa.packet;
20 +
21 +import org.onlab.packet.BasePacket;
22 +import org.onlab.packet.IPacket;
23 +
24 +import java.nio.ByteBuffer;
25 +
26 +
27 +/**
28 + *
29 + */
30 +public class EAP extends BasePacket {
31 + public static final short MIN_LEN = 0x4;
32 + public static final short EAP_HDR_LEN_REQ_RESP = 5;
33 + public static final short EAP_HDR_LEN_SUC_FAIL = 4;
34 +
35 + /* EAP Code */
36 + public static final byte REQUEST = 0x1;
37 + public static final byte RESPONSE = 0x2;
38 + public static final byte SUCCESS = 0x3;
39 + public static final byte FAILURE = 0x4;
40 +
41 + /* EAP Attribute Type */
42 + public static final byte ATTR_IDENTITY = 0x1;
43 + public static final byte ATTR_NOTIFICATION = 0x2;
44 + public static final byte ATTR_NAK = 0x3;
45 + public static final byte ATTR_MD5 = 0x4;
46 + public static final byte ATTR_OTP = 0x5;
47 + public static final byte ATTR_GTC = 0x6;
48 + public static final byte ATTR_TLS = 0xd;
49 +
50 + protected byte code;
51 + protected byte identifier;
52 + protected short length;
53 + protected byte type;
54 + protected byte[] data;
55 +
56 +
57 + /**
58 + * Get the EAP code.
59 + * @return EAP code
60 + */
61 + public byte getCode() {
62 + return this.code;
63 + }
64 +
65 +
66 + /**
67 + * Set the EAP code.
68 + * @param code EAP code
69 + * @return this
70 + */
71 + public EAP setCode(final byte code) {
72 + this.code = code;
73 + return this;
74 + }
75 +
76 + /**
77 + * Get the EAP identifier.
78 + * @return EAP identifier
79 + */
80 + public byte getIdentifier() {
81 + return this.identifier;
82 + }
83 +
84 + /**
85 + * Set the EAP identifier.
86 + * @param identifier
87 + * @return this
88 + */
89 + public EAP setIdentifier(final byte identifier) {
90 + this.identifier = identifier;
91 + return this;
92 + }
93 +
94 + /**
95 + * Get the get packet length.
96 + * @return packet length
97 + */
98 + public short getLength() {
99 + return this.length;
100 + }
101 +
102 + /**
103 + * Set the packet length.
104 + * @param length packet length
105 + * @return this
106 + */
107 + public EAP setLength(final short length) {
108 + this.length = length;
109 + return this;
110 + }
111 +
112 + /**
113 + * Get the data type.
114 + * @return data type
115 + */
116 + public byte getDataType() {
117 + return this.type;
118 + }
119 +
120 + /**
121 + * Set the data type.
122 + * @param type data type
123 + * @return this
124 + */
125 + public EAP setDataType(final byte type) {
126 + this.type = type;
127 + return this;
128 + }
129 +
130 + /**
131 + * Get the EAP data.
132 + * @return EAP data
133 + */
134 + public byte[] getData() {
135 + return this.data;
136 + }
137 +
138 + /**
139 + * Set the EAP data.
140 + * @param data EAP data to be set
141 + * @return this
142 + */
143 + public EAP setData(final byte[] data) {
144 + this.data = data;
145 + return this;
146 + }
147 +
148 + /**
149 + * Default EAP constructor that set the EAP code to 0.
150 + */
151 + public EAP() {
152 + this.code = 0;
153 + }
154 +
155 + /**
156 + * EAP constructor that initially sets all fields.
157 + * @param code EAP code
158 + * @param identifier EAP identifier
159 + * @param type packet type
160 + * @param data EAP data
161 + */
162 + public EAP(byte code, byte identifier, byte type, byte[] data) {
163 + this.code = code;
164 + this.identifier = identifier;
165 + if (this.code == REQUEST || this.code == RESPONSE) {
166 + this.length = (short) (5 + (data == null ? 0 : data.length));
167 + this.type = type;
168 + } else {
169 + this.length = (short) (4 + (data == null ? 0 : data.length));
170 + }
171 + this.data = data;
172 + }
173 +
174 + /**
175 + * Serializes the packet, based on the code/type using the payload
176 + * to compute its length.
177 + * @return the serialized payload
178 + */
179 + @Override
180 + public byte[] serialize() {
181 + final byte[] data = new byte[this.length];
182 +
183 + final ByteBuffer bb = ByteBuffer.wrap(data);
184 + bb.put(this.code);
185 + bb.put(this.identifier);
186 + bb.putShort(this.length);
187 + if (this.code == REQUEST || this.code == RESPONSE) {
188 + bb.put(this.type);
189 + }
190 + if (this.data != null) {
191 + bb.put(this.data);
192 + }
193 + return data;
194 + }
195 +
196 + @Override
197 + public IPacket deserialize(final byte[] data, final int offset,
198 + final int length) {
199 + final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
200 + this.code = bb.get();
201 + this.identifier = bb.get();
202 + this.length = bb.getShort();
203 +
204 + int dataLength;
205 + if (this.code == REQUEST || this.code == RESPONSE) {
206 + this.type = bb.get();
207 + dataLength = this.length - 5;
208 + } else {
209 + dataLength = this.length - 4;
210 + }
211 + this.data = new byte[dataLength];
212 + bb.get(this.data);
213 + return this;
214 + }
215 +
216 + @Override
217 + public int hashCode() {
218 + final int prime = 3889;
219 + int result = super.hashCode();
220 + result = prime * result + this.code;
221 + result = prime * result + this.identifier;
222 + result = prime * result + this.length;
223 + result = prime * result + this.type;
224 + return result;
225 + }
226 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 + */
16 +
17 +package org.onosproject.aaa.packet;
18 +
19 +import org.onlab.packet.ARP;
20 +import org.onlab.packet.Ethernet;
21 +import org.onlab.packet.IPv4;
22 +import org.onlab.packet.IPv6;
23 +import org.onlab.packet.LLDP;
24 +
25 +/**
26 + * Created by jono on 5/19/15.
27 + */
28 +public final class EAPEthernet extends Ethernet {
29 +
30 + public static final short TYPE_PAE = (short) 0x888e;
31 +
32 + private EAPEthernet() {
33 +
34 + }
35 +
36 + static {
37 + Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_ARP, ARP.class);
38 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_RARP, ARP.class);
39 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_IPV4, IPv4.class);
40 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_IPV6, IPv6.class);
41 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_LLDP, LLDP.class);
42 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(org.onlab.packet.Ethernet.TYPE_BSN, LLDP.class);
43 + org.onlab.packet.Ethernet.ETHER_TYPE_CLASS_MAP.put(TYPE_PAE, EAPOL.class);
44 + }
45 +}
1 +/*
2 + *
3 + * * Copyright 2015 AT&T Foundry
4 + * *
5 + * * Licensed under the Apache License, Version 2.0 (the "License");
6 + * * you may not use this file except in compliance with the License.
7 + * * You may obtain a copy of the License at
8 + * *
9 + * * http://www.apache.org/licenses/LICENSE-2.0
10 + * *
11 + * * Unless required by applicable law or agreed to in writing, software
12 + * * distributed under the License is distributed on an "AS IS" BASIS,
13 + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * * See the License for the specific language governing permissions and
15 + * * limitations under the License.
16 + *
17 + */
18 +
19 +package org.onosproject.aaa.packet;
20 +
21 +import org.onlab.packet.BasePacket;
22 +import org.onlab.packet.Ethernet;
23 +import org.onlab.packet.IPacket;
24 +import org.onlab.packet.MacAddress;
25 +
26 +import java.nio.ByteBuffer;
27 +
28 +/**
29 + *
30 + */
31 +public class EAPOL extends BasePacket {
32 +
33 + private byte version = 0x01;
34 + private byte eapolType;
35 + private short packetLength;
36 +
37 + /* EAPOL Packet Type */
38 + public static final byte EAPOL_PACKET = 0x0;
39 + public static final byte EAPOL_START = 0x1;
40 + public static final byte EAPOL_LOGOFF = 0x2;
41 + public static final byte EAPOL_KEY = 0x3;
42 + public static final byte EAPOL_ASF = 0x4;
43 +
44 + public static final MacAddress PAE_GROUP_ADDR = MacAddress.valueOf(new byte[] {
45 + (byte) 0x01, (byte) 0x80, (byte) 0xc2, (byte) 0x00, (byte) 0x00, (byte) 0x03
46 + });
47 +
48 +
49 + /**
50 + * Get version.
51 + * @return version
52 + */
53 + public byte getVersion() {
54 + return this.version;
55 + }
56 +
57 + /**
58 + * Set version.
59 + * @param version EAPOL version
60 + * @return this
61 + */
62 + public EAPOL setVersion(final byte version) {
63 + this.version = version;
64 + return this;
65 + }
66 +
67 + /**
68 + * Get type.
69 + * @return EAPOL type
70 + */
71 + public byte getEapolType() {
72 + return this.eapolType;
73 + }
74 +
75 + /**
76 + * Set EAPOL type.
77 + * @param eapolType EAPOL type
78 + * @return this
79 + */
80 + public EAPOL setEapolType(final byte eapolType) {
81 + this.eapolType = eapolType;
82 + return this;
83 + }
84 +
85 + /**
86 + * Get packet length.
87 + * @return packet length
88 + */
89 + public short getPacketLength() {
90 + return this.packetLength;
91 + }
92 +
93 + /**
94 + * Set packet length.
95 + * @param packetLen packet length
96 + * @return this
97 + */
98 + public EAPOL setPacketLength(final short packetLen) {
99 + this.packetLength = packetLen;
100 + return this;
101 + }
102 +
103 +
104 +
105 + /**
106 + * Serializes the packet, based on the code/type using the payload
107 + * to compute its length.
108 + * @return this
109 + */
110 + @Override
111 + public byte[] serialize() {
112 +
113 + byte[] payloadData = null;
114 +
115 + if (this.payload != null) {
116 + this.payload.setParent(this);
117 + payloadData = this.payload.serialize();
118 + }
119 +
120 + //prepare the buffer to hold the version (1), packet type (1), packet length (2) and the eap payload.
121 + //if there is no payload, packet length is 0
122 + byte[] data = new byte[4 + this.packetLength];
123 + final ByteBuffer bb = ByteBuffer.wrap(data);
124 + bb.put(this.version);
125 + bb.put(this.eapolType);
126 + bb.putShort(this.packetLength);
127 +
128 + //put the EAP payload
129 + if (payloadData != null) {
130 + bb.put(payloadData);
131 + }
132 +
133 + return data;
134 + }
135 +
136 + @Override
137 + public IPacket deserialize(final byte[] data, final int offset,
138 + final int length) {
139 + final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
140 +
141 +
142 + //deserialize the EAPOL header
143 + this.version = bb.get();
144 + this.eapolType = bb.get();
145 + this.packetLength = bb.getShort();
146 +
147 + if (this.packetLength > 0) {
148 + //deserialize the EAP Payload
149 + this.payload = new EAP();
150 +
151 + this.payload = this.payload.deserialize(data, bb.position(), length - 4);
152 + this.payload.setParent(this);
153 + }
154 +
155 +
156 + return this;
157 + }
158 +
159 + @Override
160 + public int hashCode() {
161 + final int prime = 3889;
162 + int result = super.hashCode();
163 + result = prime * result + this.version;
164 + result = prime * result + this.eapolType;
165 + result = prime * result + this.packetLength;
166 + return result;
167 + }
168 +
169 + /**
170 + *
171 + * @param dstMac
172 + * @param srcMac
173 + * @param eapolType
174 + * @param eap
175 + * @return Ethernet frame
176 + */
177 + public static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
178 + short vlan, byte eapolType, EAP eap) {
179 +
180 + Ethernet eth = new Ethernet();
181 + eth.setDestinationMACAddress(dstMac.toBytes());
182 + eth.setSourceMACAddress(srcMac.toBytes());
183 + eth.setEtherType(EAPEthernet.TYPE_PAE);
184 + if (vlan != Ethernet.VLAN_UNTAGGED) {
185 + eth.setVlanID(vlan);
186 + }
187 + //eapol header
188 + EAPOL eapol = new EAPOL();
189 + eapol.setEapolType(eapolType);
190 + eapol.setPacketLength(eap.getLength());
191 +
192 + //eap part
193 + eapol.setPayload(eap);
194 +
195 + eth.setPayload(eapol);
196 + eth.setPad(true);
197 + return eth;
198 + }
199 +}
200 +
1 +/*
2 + *
3 + * * Copyright 2015 AT&T Foundry
4 + * *
5 + * * Licensed under the Apache License, Version 2.0 (the "License");
6 + * * you may not use this file except in compliance with the License.
7 + * * You may obtain a copy of the License at
8 + * *
9 + * * http://www.apache.org/licenses/LICENSE-2.0
10 + * *
11 + * * Unless required by applicable law or agreed to in writing, software
12 + * * distributed under the License is distributed on an "AS IS" BASIS,
13 + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * * See the License for the specific language governing permissions and
15 + * * limitations under the License.
16 + *
17 + */
18 +
19 +package org.onosproject.aaa.packet;
20 +
21 +import org.onlab.packet.BasePacket;
22 +import org.onlab.packet.IPacket;
23 +import org.slf4j.Logger;
24 +
25 +import javax.crypto.Mac;
26 +import javax.crypto.spec.SecretKeySpec;
27 +import java.io.ByteArrayOutputStream;
28 +import java.io.IOException;
29 +import java.nio.ByteBuffer;
30 +import java.security.SecureRandom;
31 +import java.util.ArrayList;
32 +import java.util.Arrays;
33 +
34 +import static org.slf4j.LoggerFactory.getLogger;
35 +
36 +/**
37 + *
38 + */
39 +public class RADIUS extends BasePacket {
40 + protected byte code;
41 + protected byte identifier;
42 + protected short length = RADIUS_MIN_LENGTH;
43 + protected byte[] authenticator = new byte[16];
44 + protected ArrayList<RADIUSAttribute> attributes = new ArrayList<>();
45 +
46 + /* RADIUS parameters */
47 + public static final short RADIUS_MIN_LENGTH = 20;
48 + public static final short MAX_ATTR_VALUE_LENGTH = 253;
49 + public static final short RADIUS_MAX_LENGTH = 4096;
50 +
51 + /* RADIUS packet types */
52 + public static final byte RADIUS_CODE_ACCESS_REQUEST = 0x01;
53 + public static final byte RADIUS_CODE_ACCESS_ACCEPT = 0x02;
54 + public static final byte RADIUS_CODE_ACCESS_REJECT = 0x03;
55 + public static final byte RADIUS_CODE_ACCOUNTING_REQUEST = 0x04;
56 + public static final byte RADIUS_CODE_ACCOUNTING_RESPONSE = 0x05;
57 + public static final byte RADIUS_CODE_ACCESS_CHALLENGE = 0x0b;
58 +
59 + private final Logger log = getLogger(getClass());
60 +
61 + public RADIUS() {
62 + }
63 +
64 + public RADIUS(byte code, byte identifier) {
65 + this.code = code;
66 + this.identifier = identifier;
67 + }
68 +
69 + public byte getCode() {
70 + return this.code;
71 + }
72 +
73 + public void setCode(byte code) {
74 + this.code = code;
75 + }
76 +
77 + public byte getIdentifier() {
78 + return this.identifier;
79 + }
80 +
81 + public void setIdentifier(byte identifier) {
82 + this.identifier = identifier;
83 + }
84 +
85 + public byte[] getAuthenticator() {
86 + return this.authenticator;
87 + }
88 +
89 + public void setAuthenticator(byte[] a) {
90 + this.authenticator = a;
91 + }
92 +
93 + public byte[] generateAuthCode() {
94 + new SecureRandom().nextBytes(this.authenticator);
95 + return this.authenticator;
96 + }
97 +
98 + public boolean isValidCode() {
99 + return this.code == RADIUS_CODE_ACCESS_REQUEST ||
100 + this.code == RADIUS_CODE_ACCESS_ACCEPT ||
101 + this.code == RADIUS_CODE_ACCESS_REJECT ||
102 + this.code == RADIUS_CODE_ACCOUNTING_REQUEST ||
103 + this.code == RADIUS_CODE_ACCOUNTING_RESPONSE ||
104 + this.code == RADIUS_CODE_ACCESS_CHALLENGE;
105 + }
106 +
107 + public RADIUSAttribute addMessageAuthenticator(String key) {
108 + /* Message-Authenticator = HMAC-MD5 (Type, Identifier, Length, Request Authenticator, Attributes)
109 + When the message integrity check is calculated the signature string should be considered to be
110 + sixteen octets of zero.
111 + */
112 + byte[] hashOutput = new byte[16];
113 + Arrays.fill(hashOutput, (byte) 0);
114 +
115 + RADIUSAttribute authAttribute = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH);
116 + if (authAttribute != null) {
117 + // If Message-Authenticator was already present, override it
118 + this.log.warn("Attempted to add duplicate Message-Authenticator");
119 + authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
120 + } else {
121 + // Else generate a new attribute padded with zeroes
122 + authAttribute = this.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
123 + }
124 + // Calculate the MD5 HMAC based on the message
125 + try {
126 + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
127 + Mac mac = Mac.getInstance("HmacMD5");
128 + mac.init(keySpec);
129 + hashOutput = mac.doFinal(this.serialize());
130 + // Update HMAC in Message-Authenticator
131 + authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
132 + } catch (Exception e) {
133 + this.log.error("Failed to generate message authenticator: {}", e.getMessage());
134 + }
135 +
136 + return authAttribute;
137 + }
138 +
139 + public boolean checkMessageAuthenticator(String key) {
140 + byte[] newHash = new byte[16];
141 + Arrays.fill(newHash, (byte) 0);
142 + byte[] messageAuthenticator = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH).getValue();
143 + this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, newHash);
144 + // Calculate the MD5 HMAC based on the message
145 + try {
146 + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
147 + Mac mac = Mac.getInstance("HmacMD5");
148 + mac.init(keySpec);
149 + newHash = mac.doFinal(this.serialize());
150 + } catch (Exception e) {
151 + log.error("Failed to generate message authenticator: {}", e.getMessage());
152 + }
153 + this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, messageAuthenticator);
154 + // Compare the calculated Message-Authenticator with the one in the message
155 + return Arrays.equals(newHash, messageAuthenticator);
156 + }
157 +
158 + /**
159 + * @param message
160 + * EAP message object to be embedded in the RADIUS EAP-Message attributed
161 + */
162 + public void encapsulateMessage(EAP message) {
163 + if (message.length <= MAX_ATTR_VALUE_LENGTH) {
164 + // Use the regular serialization method as it fits into one EAP-Message attribute
165 + this.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
166 + message.serialize());
167 + } else {
168 + // Segment the message into chucks and embed them in several EAP-Message attributes
169 + short remainingLength = message.length;
170 + byte[] messageBuffer = message.serialize();
171 + final ByteBuffer bb = ByteBuffer.wrap(messageBuffer);
172 + while (bb.hasRemaining()) {
173 + byte[] messageAttributeData;
174 + if (remainingLength > MAX_ATTR_VALUE_LENGTH) {
175 + // The remaining data is still too long to fit into one attribute, keep going
176 + messageAttributeData = new byte[MAX_ATTR_VALUE_LENGTH];
177 + bb.get(messageAttributeData, 0, MAX_ATTR_VALUE_LENGTH);
178 + remainingLength -= MAX_ATTR_VALUE_LENGTH;
179 + } else {
180 + // The remaining data fits, this will be the last chunk
181 + messageAttributeData = new byte[remainingLength];
182 + bb.get(messageAttributeData, 0, remainingLength);
183 + }
184 + this.attributes.add(new RADIUSAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
185 + (byte) (messageAttributeData.length + 2), messageAttributeData));
186 +
187 + // Adding the size of the data to the total RADIUS length
188 + this.length += (short) (messageAttributeData.length & 0xFF);
189 + // Adding the size of the overhead attribute type and length
190 + this.length += 2;
191 + }
192 + }
193 + }
194 +
195 + /**
196 + * @return An EAP object containing the reassembled EAP message
197 + */
198 + public EAP decapsulateMessage() {
199 + EAP message = new EAP();
200 + ByteArrayOutputStream messageStream = new ByteArrayOutputStream();
201 + // Iterating through EAP-Message attributes to concatenate their value
202 + for (RADIUSAttribute ra : this.getAttributeList(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE)) {
203 + try {
204 + messageStream.write(ra.getValue());
205 + } catch (IOException e) {
206 + log.error("Error while reassembling EAP message: {}", e.getMessage());
207 + }
208 + }
209 + // Assembling EAP object from the concatenated stream
210 + message.deserialize(messageStream.toByteArray(), 0, messageStream.size());
211 + return message;
212 + }
213 +
214 + /**
215 + * @param attrType
216 + * the type field of the required attributes
217 + * @return List of the attributes that matches the type or an empty list if there is none
218 + */
219 + public ArrayList<RADIUSAttribute> getAttributeList(byte attrType) {
220 + ArrayList<RADIUSAttribute> attrList = new ArrayList<>();
221 + for (int i = 0; i < this.attributes.size(); i++) {
222 + if (this.attributes.get(i).getType() == attrType) {
223 + attrList.add(this.attributes.get(i));
224 + }
225 + }
226 + return attrList;
227 + }
228 +
229 + /**
230 + * @param attrType
231 + * the type field of the required attribute
232 + * @return the first attribute that matches the type or null if does not exist
233 + */
234 + public RADIUSAttribute getAttribute(byte attrType) {
235 + for (int i = 0; i < this.attributes.size(); i++) {
236 + if (this.attributes.get(i).getType() == attrType) {
237 + return this.attributes.get(i);
238 + }
239 + }
240 + return null;
241 + }
242 +
243 + /**
244 + * @param attrType
245 + * the type field of the attribute to set
246 + * @param value
247 + * value to be set
248 + * @return reference to the attribute object
249 + */
250 + public RADIUSAttribute setAttribute(byte attrType, byte[] value) {
251 + byte attrLength = (byte) (value.length + 2);
252 + RADIUSAttribute newAttribute = new RADIUSAttribute(attrType, attrLength, value);
253 + this.attributes.add(newAttribute);
254 + this.length += (short) (attrLength & 0xFF);
255 + return newAttribute;
256 + }
257 +
258 + public RADIUSAttribute updateAttribute(byte attrType, byte[] value) {
259 + for (int i = 0; i < this.attributes.size(); i++) {
260 + if (this.attributes.get(i).getType() == attrType) {
261 + this.length -= (short) (this.attributes.get(i).getLength() & 0xFF);
262 + RADIUSAttribute newAttr = new RADIUSAttribute(attrType, (byte) (value.length + 2), value);
263 + this.attributes.set(i, newAttr);
264 + this.length += (short) (newAttr.getLength() & 0xFF);
265 + return newAttr;
266 + }
267 + }
268 + return null;
269 + }
270 +
271 + @Override
272 + public byte[] serialize() {
273 + final byte[] data = new byte[this.length];
274 + final ByteBuffer bb = ByteBuffer.wrap(data);
275 +
276 + bb.put(this.code);
277 + bb.put(this.identifier);
278 + bb.putShort(this.length);
279 + bb.put(this.authenticator);
280 + for (int i = 0; i < this.attributes.size(); i++) {
281 + RADIUSAttribute attr = this.attributes.get(i);
282 + bb.put(attr.getType());
283 + bb.put(attr.getLength());
284 + bb.put(attr.getValue());
285 + }
286 +
287 + return data;
288 + }
289 +
290 + @Override
291 + public IPacket deserialize(final byte[] data, final int offset,
292 + final int length) {
293 + final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
294 + this.code = bb.get();
295 + this.identifier = bb.get();
296 + this.length = bb.getShort();
297 + bb.get(this.authenticator, 0, 16);
298 +
299 + int remainingLength = this.length - RADIUS_MIN_LENGTH;
300 + while (remainingLength > 0 && bb.hasRemaining()) {
301 + RADIUSAttribute attr = new RADIUSAttribute();
302 + attr.setType(bb.get());
303 + attr.setLength(bb.get());
304 + short attrLength = (short) (attr.length & 0xff);
305 + attr.value = new byte[attrLength - 2];
306 + bb.get(attr.value, 0, attrLength - 2);
307 + this.attributes.add(attr);
308 + remainingLength -= attr.length;
309 + }
310 + return this;
311 + }
312 +
313 +}
1 +/*
2 + *
3 + * * Copyright 2015 AT&T Foundry
4 + * *
5 + * * Licensed under the Apache License, Version 2.0 (the "License");
6 + * * you may not use this file except in compliance with the License.
7 + * * You may obtain a copy of the License at
8 + * *
9 + * * http://www.apache.org/licenses/LICENSE-2.0
10 + * *
11 + * * Unless required by applicable law or agreed to in writing, software
12 + * * distributed under the License is distributed on an "AS IS" BASIS,
13 + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * * See the License for the specific language governing permissions and
15 + * * limitations under the License.
16 + *
17 + */
18 +
19 +package org.onosproject.aaa.packet;
20 +
21 +import java.nio.ByteBuffer;
22 +
23 +public class RADIUSAttribute {
24 + protected byte type;
25 + protected byte length;
26 + protected byte[] value;
27 +
28 + /* RADIUS attribute types */
29 + public static final byte RADIUS_ATTR_USERNAME = 1;
30 + public static final byte RADIUS_ATTR_NAS_IP = 4;
31 + public static final byte RADIUS_ATTR_NAS_PORT = 5;
32 + public static final byte RADIUS_ATTR_FRAMED_MTU = 12;
33 + public static final byte RADIUS_ATTR_STATE = 24;
34 + public static final byte RADIUS_ATTR_VENDOR_SPECIFIC = 26;
35 + public static final byte RADIUS_ATTR_CALLING_STATION_ID = 31;
36 + public static final byte RADIUS_ATTR_NAS_ID = 32;
37 + public static final byte RADIUS_ATTR_ACCT_SESSION_ID = 44;
38 + public static final byte RADIUS_ATTR_NAS_PORT_TYPE = 61;
39 + public static final byte RADIUS_ATTR_EAP_MESSAGE = 79;
40 + public static final byte RADIUS_ATTR_MESSAGE_AUTH = 80;
41 + public static final byte RADIUS_ATTR_NAS_PORT_ID = 87;
42 +
43 + public RADIUSAttribute() {
44 + }
45 +
46 + public RADIUSAttribute(final byte type, final byte length, final byte[] value) {
47 + this.type = type;
48 + this.length = length;
49 + this.value = value;
50 + }
51 +
52 + public boolean isValidType() {
53 + return this.type == RADIUS_ATTR_USERNAME ||
54 + this.type == RADIUS_ATTR_NAS_IP ||
55 + this.type == RADIUS_ATTR_NAS_PORT ||
56 + this.type == RADIUS_ATTR_VENDOR_SPECIFIC ||
57 + this.type == RADIUS_ATTR_CALLING_STATION_ID ||
58 + this.type == RADIUS_ATTR_NAS_ID ||
59 + this.type == RADIUS_ATTR_ACCT_SESSION_ID ||
60 + this.type == RADIUS_ATTR_NAS_PORT_TYPE ||
61 + this.type == RADIUS_ATTR_EAP_MESSAGE ||
62 + this.type == RADIUS_ATTR_MESSAGE_AUTH ||
63 + this.type == RADIUS_ATTR_NAS_PORT_ID;
64 + }
65 +
66 + /**
67 + * @return the type
68 + */
69 + public byte getType() {
70 + return this.type;
71 + }
72 +
73 + /**
74 + * @param type
75 + * the code to set
76 + * @return this
77 + */
78 + public RADIUSAttribute setType(final byte type) {
79 + this.type = type;
80 + return this;
81 + }
82 +
83 + /**
84 + * @return the length
85 + */
86 + public byte getLength() {
87 + return this.length;
88 + }
89 +
90 + /**
91 + * @param length
92 + * the length to set
93 + * @return this
94 + */
95 + public RADIUSAttribute setLength(final byte length) {
96 + this.length = length;
97 + return this;
98 + }
99 +
100 + /**
101 + * @return the value
102 + */
103 + public byte[] getValue() {
104 + return this.value;
105 + }
106 +
107 + /**
108 + * @param value
109 + * the data to set
110 + * @return this
111 + */
112 + public RADIUSAttribute setValue(final byte[] value) {
113 + this.value = value;
114 + return this;
115 + }
116 +
117 + public byte[] serialize() {
118 + final byte[] data = new byte[this.length];
119 + final ByteBuffer bb = ByteBuffer.wrap(data);
120 + bb.put(this.type);
121 + bb.put(this.length);
122 + bb.put(this.value);
123 + return data;
124 + }
125 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
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 + */
16 +package org.onosproject.aaa;
17 +
18 +import org.junit.After;
19 +import org.junit.Before;
20 +import org.junit.Test;
21 +
22 +/**
23 + * Set of tests of the ONOS application component.
24 + */
25 +public class AAATest {
26 +
27 + private AAA aaa;
28 +
29 + @Before
30 + public void setUp() {
31 +
32 + }
33 +
34 + @After
35 + public void tearDown() {
36 + }
37 +
38 + @Test
39 + public void basics() {
40 +
41 + }
42 +
43 +}
1 +/*
2 + *
3 + * Copyright 2015 AT&T Foundry
4 + *
5 + * Licensed under the Apache License, Version 2.0 (the "License");
6 + * you may not use this file except in compliance with the License.
7 + * You may obtain a copy of the License at
8 + *
9 + * http://www.apache.org/licenses/LICENSE-2.0
10 + *
11 + * Unless required by applicable law or agreed to in writing, software
12 + * distributed under the License is distributed on an "AS IS" BASIS,
13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + * See the License for the specific language governing permissions and
15 + * limitations under the License.
16 + *
17 + */
18 +package org.onosproject.aaa;
19 +
20 +import org.junit.After;
21 +import org.junit.Assert;
22 +import org.junit.Before;
23 +import org.junit.Test;
24 +
25 +
26 +public class StateMachineTest {
27 + StateMachine stateMachine = null;
28 +
29 + @Before
30 + public void setUp() {
31 + System.out.println("Set Up.");
32 + StateMachine.bitSet.clear();
33 + stateMachine = new StateMachine("session0", null);
34 + }
35 +
36 + @After
37 + public void tearDown() {
38 + System.out.println("Tear Down.");
39 + StateMachine.bitSet.clear();
40 + stateMachine = null;
41 + }
42 +
43 + @Test
44 + /**
45 + * Test all the basic inputs from state to state: IDLE -> STARTED -> PENDING -> AUTHORIZED -> IDLE
46 + */
47 + public void basic() throws StateMachineException {
48 + System.out.println("======= BASIC =======.");
49 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
50 +
51 + stateMachine.start();
52 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
53 +
54 + stateMachine.requestAccess();
55 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
56 +
57 + stateMachine.authorizeAccess();
58 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
59 +
60 + stateMachine.logoff();
61 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
62 + }
63 +
64 + @Test
65 + /**
66 + * Test all inputs from an IDLE state (starting with the ones that are not impacting the current state)
67 + */
68 + public void testIdleState() throws StateMachineException {
69 + System.out.println("======= IDLE STATE TEST =======.");
70 + stateMachine.requestAccess();
71 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
72 +
73 + stateMachine.authorizeAccess();
74 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
75 +
76 + stateMachine.denyAccess();
77 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
78 +
79 + stateMachine.logoff();
80 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
81 +
82 + stateMachine.start();
83 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
84 + }
85 +
86 + @Test
87 + /**
88 + * Test all inputs from an STARTED state (starting with the ones that are not impacting the current state)
89 + */
90 + public void testStartedState() throws StateMachineException {
91 + System.out.println("======= STARTED STATE TEST =======.");
92 + stateMachine.start();
93 +
94 + stateMachine.authorizeAccess();
95 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
96 +
97 + stateMachine.denyAccess();
98 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
99 +
100 + stateMachine.logoff();
101 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
102 +
103 + stateMachine.start();
104 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
105 +
106 + stateMachine.requestAccess();
107 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
108 + }
109 +
110 + @Test
111 + /**
112 + * Test all inputs from a PENDING state (starting with the ones that are not impacting the current state).
113 + * The next valid state for this test is AUTHORIZED
114 + */
115 + public void testPendingStateToAuthorized() throws StateMachineException {
116 + System.out.println("======= PENDING STATE TEST (AUTHORIZED) =======.");
117 + stateMachine.start();
118 + stateMachine.requestAccess();
119 +
120 + stateMachine.logoff();
121 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
122 +
123 + stateMachine.start();
124 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
125 +
126 + stateMachine.requestAccess();
127 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
128 +
129 + stateMachine.authorizeAccess();
130 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
131 +
132 + stateMachine.denyAccess();
133 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
134 + }
135 +
136 + @Test
137 + /**
138 + * Test all inputs from an PENDING state (starting with the ones that are not impacting the current state).
139 + * The next valid state for this test is UNAUTHORIZED
140 + */
141 + public void testPendingStateToUnauthorized() throws StateMachineException {
142 + System.out.println("======= PENDING STATE TEST (DENIED) =======.");
143 + stateMachine.start();
144 + stateMachine.requestAccess();
145 +
146 + stateMachine.logoff();
147 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
148 +
149 + stateMachine.start();
150 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
151 +
152 + stateMachine.requestAccess();
153 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
154 +
155 + stateMachine.denyAccess();
156 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
157 +
158 + stateMachine.authorizeAccess();
159 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
160 + }
161 +
162 + @Test
163 + /**
164 + * Test all inputs from an AUTHORIZED state (starting with the ones that are not impacting the current state).
165 + */
166 + public void testAuthorizedState() throws StateMachineException {
167 + System.out.println("======= AUTHORIZED STATE TEST =======.");
168 + stateMachine.start();
169 + stateMachine.requestAccess();
170 + stateMachine.authorizeAccess();
171 +
172 + stateMachine.start();
173 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
174 +
175 + stateMachine.requestAccess();
176 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
177 +
178 + stateMachine.authorizeAccess();
179 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
180 +
181 + stateMachine.denyAccess();
182 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
183 +
184 + stateMachine.logoff();
185 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
186 + }
187 +
188 + @Test
189 + /**
190 + * Test all inputs from an UNAUTHORIZED state (starting with the ones that are not impacting the current state).
191 + */
192 + public void testUnauthorizedState() throws StateMachineException {
193 + System.out.println("======= UNAUTHORIZED STATE TEST =======.");
194 + stateMachine.start();
195 + stateMachine.requestAccess();
196 + stateMachine.denyAccess();
197 +
198 + stateMachine.start();
199 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
200 +
201 + stateMachine.requestAccess();
202 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
203 +
204 + stateMachine.authorizeAccess();
205 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
206 +
207 + stateMachine.denyAccess();
208 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
209 +
210 + stateMachine.logoff();
211 + Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
212 + }
213 +
214 +
215 + @Test
216 + public void testIdentifierAvailability() throws StateMachineException {
217 + System.out.println("======= IDENTIFIER TEST =======.");
218 + byte identifier = stateMachine.getIdentifier();
219 + System.out.println("State: " + stateMachine.getState());
220 + System.out.println("Identifier: " + Byte.toUnsignedInt(identifier));
221 + Assert.assertEquals(-1, identifier);
222 + stateMachine.start();
223 +
224 +
225 + StateMachine sm247 = null;
226 + StateMachine sm3 = null;
227 +
228 +
229 + //create 255 others state machines
230 + for (int i = 1; i <= 255; i++) {
231 + StateMachine sm = new StateMachine("session" + i, null);
232 + sm.start();
233 + byte id = sm.getIdentifier();
234 + Assert.assertEquals(i, Byte.toUnsignedInt(id));
235 + if (i == 3) {
236 + sm3 = sm;
237 + System.out.println("SM3: " + sm3.toString());
238 + }
239 + if (i == 247) {
240 + sm247 = sm;
241 + System.out.println("SM247: " + sm247.toString());
242 + }
243 + }
244 +
245 + //simulate the state machine for a specific session and logoff so we can free up a spot for an identifier
246 + //let's choose identifier 247 then we free up 3
247 + sm247.requestAccess();
248 + sm247.authorizeAccess();
249 + sm247.logoff();
250 + sm247 = null;
251 +
252 + sm3.requestAccess();
253 + sm3.authorizeAccess();
254 + sm3.logoff();
255 + sm3 = null;
256 +
257 + StateMachine otherSM3 = new StateMachine("session3b", null);
258 + otherSM3.start();
259 + otherSM3.requestAccess();
260 + byte id3 = otherSM3.getIdentifier();
261 + Assert.assertEquals(3, Byte.toUnsignedInt(id3));
262 +
263 + StateMachine otherSM247 = new StateMachine("session247b", null);
264 + otherSM247.start();
265 + otherSM247.requestAccess();
266 + byte id247 = otherSM247.getIdentifier();
267 + Assert.assertEquals(247, Byte.toUnsignedInt(id247));
268 +
269 + }
270 +}
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
32 <description>ONOS sample applications</description> 32 <description>ONOS sample applications</description>
33 33
34 <modules> 34 <modules>
35 + <module>aaa</module>
35 <module>fwd</module> 36 <module>fwd</module>
36 <module>mobility</module> 37 <module>mobility</module>
37 <module>proxyarp</module> 38 <module>proxyarp</module>
......