Committed by
Gerrit Code Review
ONOS AAA app: Authentication and Authorization logic.
Change-Id: I36eb889eeab38edf12377c13e780a147551459a4
Showing
14 changed files
with
2409 additions
and
0 deletions
apps/aaa/app.xml
0 → 100644
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> |
apps/aaa/features.xml
0 → 100644
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> |
apps/aaa/pom.xml
0 → 100644
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> | ... | ... |
-
Please register or login to post a comment