blob: cf53b686496dab1c73aecf55b309aa2c7f704510 [file] [log] [blame]
Amit Ghoshc9ac1e52017-07-28 12:31:18 +01001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.opencord.aaa;
17
18import org.onlab.packet.ARP;
19import org.onlab.packet.DeserializationException;
20import org.onlab.packet.EthType;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.IPv4;
24import org.onlab.packet.MacAddress;
25import org.onlab.packet.RADIUS;
26import org.onlab.packet.TpPort;
27import org.onlab.packet.UDP;
28
29import org.onosproject.core.ApplicationId;
30import org.onosproject.mastership.MastershipEvent;
31import org.onosproject.mastership.MastershipListener;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.device.DeviceEvent;
35import org.onosproject.net.device.DeviceListener;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.packet.DefaultOutboundPacket;
42import org.onosproject.net.packet.InboundPacket;
43import org.onosproject.net.packet.OutboundPacket;
44import org.onosproject.net.packet.PacketContext;
45import org.onosproject.net.packet.PacketService;
46
47import org.opencord.sadis.SubscriberAndDeviceInformation;
48import org.opencord.sadis.SubscriberAndDeviceInformationService;
49
50import org.slf4j.Logger;
51
52import com.google.common.collect.Maps;
53
54import static org.onosproject.net.packet.PacketPriority.CONTROL;
55import static org.slf4j.LoggerFactory.getLogger;
56
57import java.net.InetAddress;
58import java.nio.ByteBuffer;
59import java.util.Map;
60import java.util.Set;
61
62/**
63 * Handles communication with the RADIUS server through ports
64 * of the SDN switches.
65 */
66public class PortBasedRadiusCommunicator implements RadiusCommunicator {
67
68 // for verbose output
69 private final Logger log = getLogger(getClass());
70
71 // our unique identifier
72 private ApplicationId appId;
73
74 // to receive Packet-in events that we'll respond to
75 PacketService packetService;
76
77 DeviceService deviceService;
78
79 MastershipService mastershipService;
80
81 SubscriberAndDeviceInformationService subsService;
82
83 // to store local mapping of IP Address and Serial No of Device
84 private Map<Ip4Address, String> ipToSnMap;
85
86 // connect points to the RADIUS server
87 Set<ConnectPoint> radiusConnectPoints;
88
89 // Parsed RADIUS server addresses
90 protected InetAddress radiusIpAddress;
91
92 // RADIUS server TCP port number
93 protected short radiusServerPort;
94
95 protected String radiusMacAddress;
96
97 // NAS IP address
98 protected InetAddress nasIpAddress;
99
100 protected String nasMacAddress;
101
102 // RADIUS server Vlan ID
103 private short radiusVlanID;
104
105 // RADIUS p-bit
106 private byte radiusPBit;
107
108 PacketCustomizer pktCustomizer;
109 AaaManager aaaManager;
110
111 ConnectPoint radiusServerConnectPoint = null;
112
113 InnerMastershipListener changeListener = new InnerMastershipListener();
114 InnerDeviceListener deviceListener = new InnerDeviceListener();
115
116 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
117 MastershipService masService, DeviceService devService,
118 SubscriberAndDeviceInformationService subsService,
119 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
120 this.appId = appId;
121 this.packetService = pktService;
122 this.mastershipService = masService;
123 this.deviceService = devService;
124 this.subsService = subsService;
125 this.pktCustomizer = pktCustomizer;
126 this.aaaManager = aaaManager;
127
128 ipToSnMap = Maps.newConcurrentMap();
129 mastershipService.addListener(changeListener);
130 deviceService.addListener(deviceListener);
131
Amit Ghoshcf39d972017-08-16 08:15:20 +0100132 log.info("Created PortBased");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100133 }
134
135 private void initializeLocalState() {
136 synchronized (this) {
137 radiusServerConnectPoint = null;
138 if (radiusConnectPoints != null) {
139 // find a connect point through a device for which we are master
140 for (ConnectPoint cp: radiusConnectPoints) {
141 if (mastershipService.isLocalMaster(cp.deviceId())) {
142 if (deviceService.isAvailable(cp.deviceId())) {
143 radiusServerConnectPoint = cp;
144 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100145 break;
146 }
147 }
148 }
149
Amit Ghoshcf39d972017-08-16 08:15:20 +0100150 log.info("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100151
152 if (radiusServerConnectPoint == null) {
153 log.error("Master of none, can't send radius Message to server");
154 }
155 }
156 }
157
158 @Override
159 public void initializeLocalState(AaaConfig newCfg) {
160 if (newCfg.nasIp() != null) {
161 nasIpAddress = newCfg.nasIp();
162 }
163 if (newCfg.radiusIp() != null) {
164 radiusIpAddress = newCfg.radiusIp();
165 }
166 if (newCfg.radiusMac() != null) {
167 radiusMacAddress = newCfg.radiusMac();
168 }
169 if (newCfg.nasMac() != null) {
170 nasMacAddress = newCfg.nasMac();
171 }
172
173 radiusServerPort = newCfg.radiusServerUdpPort();
174 radiusVlanID = newCfg.radiusServerVlanId();
175 radiusPBit = newCfg.radiusServerPBit();
176
177 radiusConnectPoints = newCfg.radiusServerConnectPoints();
178
179 initializeLocalState();
180 }
181
182 @Override
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530183 public void clearLocalState() {
184 mastershipService.removeListener(changeListener);
185 deviceService.removeListener(deviceListener);
186 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100187
188 @Override
189 public void deactivate() {
190 mastershipService.removeListener(changeListener);
191 deviceService.removeListener(deviceListener);
192 }
193
194 @Override
195 public void requestIntercepts() {
196 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
197 .matchEthType(Ethernet.TYPE_ARP);
198 packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
199
200 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
201 .matchEthType(Ethernet.TYPE_IPV4)
202 .matchIPProtocol(IPv4.PROTOCOL_UDP)
203 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
204 packetService.requestPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530205
206 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
207 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
208 packetService.requestPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100209 }
210
211 @Override
212 public void withdrawIntercepts() {
213 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
214 .matchEthType(Ethernet.TYPE_ARP);
215 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
216
217 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
218 .matchEthType(Ethernet.TYPE_IPV4)
219 .matchIPProtocol(IPv4.PROTOCOL_UDP)
220 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
221 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530222
223 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
224 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
225 packetService.cancelPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100226 }
227
228 @Override
229 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
230 // create the packet
231 Ethernet ethReply = new Ethernet();
232 ethReply.setSourceMACAddress(nasMacAddress);
233 ethReply.setDestinationMACAddress(radiusMacAddress);
234 ethReply.setEtherType(Ethernet.TYPE_IPV4);
235 ethReply.setVlanID(radiusVlanID);
236 ethReply.setPriorityCode(radiusPBit);
237
238 IPv4 ipv4Packet = new IPv4();
239 ipv4Packet.setTtl((byte) 64);
240 ipv4Packet.setSourceAddress(Ip4Address.
241 valueOf(nasIpAddress).toInt());
242 ipv4Packet.setDestinationAddress(Ip4Address.
243 valueOf(radiusIpAddress).toInt());
244
245 UDP udpPacket = new UDP();
246 udpPacket.setSourcePort(radiusServerPort);
247 udpPacket.setDestinationPort(radiusServerPort);
248
249 udpPacket.setPayload(radiusPacket);
250 ipv4Packet.setPayload(udpPacket);
251 ethReply.setPayload(ipv4Packet);
252
253 // store the IP address and SN of the device, later to be used
254 // for ARP responses
255 String serialNo = deviceService.getDevice(inPkt.
256 receivedFrom().deviceId()).serialNumber();
257
258 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
259
260 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100261 log.warn("No Device found with SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100262 return;
263 }
264 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
265
266 // send the message out
267 sendFromRadiusServerPort(pktCustomizer.
268 customizeEthernetIPHeaders(ethReply, inPkt));
269 }
270
271 /**
272 * Sends packet to the RADIUS server using one of the switch ports.
273 *
274 * @param packet Ethernet packet to be sent
275 */
276 private void sendFromRadiusServerPort(Ethernet packet) {
277 if (radiusServerConnectPoint != null) {
278 log.trace("AAA Manager sending Ethernet packet = {}", packet);
279 TrafficTreatment t = DefaultTrafficTreatment.builder()
280 .setOutput(radiusServerConnectPoint.port()).build();
281 OutboundPacket o = new DefaultOutboundPacket(
282 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
283 packetService.emit(o);
284 } else {
285 log.error("Unable to send RADIUS packet, connectPoint is null");
286 }
287 }
288
289 @Override
290 public void handlePacketFromServer(PacketContext context) {
291 // Extract the original Ethernet frame from the packet information
292 InboundPacket pkt = context.inPacket();
293 Ethernet ethPkt = pkt.parsed();
294 if (ethPkt == null) {
295 return;
296 }
297
298 // identify if incoming packet
299 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
300 case ARP:
301 handleArpPacketFromServer(context);
302 break;
303 case IPV4:
304 handleIPv4PacketFromServer(context);
305 break;
306 default:
307 log.debug("Skipping Ethernet packet type {}",
308 EthType.EtherType.lookup(ethPkt.getEtherType()));
309 }
310 }
311
312 /**
313 * Handles ARP packets from RADIUS server.
314 *
315 * @param context Context for the packet
316 */
317 private void handleArpPacketFromServer(PacketContext context) {
318 // Extract the original Ethernet frame from the packet information
319 InboundPacket pkt = context.inPacket();
320 Ethernet ethPkt = pkt.parsed();
321 if (ethPkt == null) {
322 return;
323 }
324
325 ARP arpPacket = (ARP) ethPkt.getPayload();
326
327 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
328 getTargetProtocolAddress());
329
330 String serialNo = ipToSnMap.get(targetAddress);
331 if (serialNo == null) {
332 log.info("No mapping found for ARP reply, target address {}",
333 targetAddress);
334 return;
335 }
336 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
337 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100338 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100339 return;
340 }
341
342 ARP arpReply = (ARP) arpPacket.clone();
343 arpReply.setOpCode(ARP.OP_REPLY);
344 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
345 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
346 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
347 arpReply.setSenderHardwareAddress(senderMac.toBytes());
348
349 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
350
351 // Ethernet Frame.
352 Ethernet ethReply = new Ethernet();
353 ethReply.setSourceMACAddress(senderMac);
354 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
355 ethReply.setEtherType(Ethernet.TYPE_ARP);
356 ethReply.setVlanID(radiusVlanID);
357 ethReply.setPriorityCode(ethPkt.getPriorityCode());
358
359 ethReply.setPayload(arpReply);
360 sendFromRadiusServerPort(ethReply);
361 }
362
363 /**
364 * Handles IP packets from RADIUS server.
365 *
366 * @param context Context for the packet
367 */
368 private void handleIPv4PacketFromServer(PacketContext context) {
369 // Extract the original Ethernet frame from the packet information
370 InboundPacket pkt = context.inPacket();
371 Ethernet ethPkt = pkt.parsed();
372 if (ethPkt == null) {
373 return;
374 }
375
376 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
377
378 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
379 UDP udpPacket = (UDP) ipv4Packet.getPayload();
380
381 if (udpPacket.getSourcePort() == radiusServerPort) {
382 //This packet is RADIUS packet from the server.
383 RADIUS radiusMsg;
384 try {
385 radiusMsg =
386 RADIUS.deserializer()
387 .deserialize(udpPacket.serialize(),
388 8,
389 udpPacket.getLength() - 8);
390 try {
391 aaaManager.handleRadiusPacket(radiusMsg);
392 } catch (StateMachineException sme) {
393 log.error("Illegal state machine operation", sme);
394 }
395 } catch (DeserializationException dex) {
396 log.error("Cannot deserialize packet", dex);
397 }
398 }
399 }
400 }
401
402 /**
403 * Handles Mastership changes for the devices which connect
404 * to the RADIUS server.
405 */
406 private class InnerMastershipListener implements MastershipListener {
407 @Override
408 public void event(MastershipEvent event) {
409 if (radiusServerConnectPoint != null &&
410 radiusServerConnectPoint.deviceId().
411 equals(event.subject())) {
412 log.trace("Mastership Event recevived for {}", event.subject());
413 // mastership of the device for our connect point has changed
414 // reselect
415 initializeLocalState();
416 }
417 }
418 }
419
420 /**
421 * Handles Device status change for the devices which connect
422 * to the RADIUS server.
423 */
424 private class InnerDeviceListener implements DeviceListener {
425 @Override
426 public void event(DeviceEvent event) {
427 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
428 if (radiusServerConnectPoint == null) {
429 switch (event.type()) {
430 case DEVICE_ADDED:
431 case DEVICE_AVAILABILITY_CHANGED:
432 // some device is available check if we can get one
433 initializeLocalState();
434 break;
435 default:
436 break;
437 }
438 return;
439 }
440 if (radiusServerConnectPoint.deviceId().
441 equals(event.subject().id())) {
442 switch (event.type()) {
443 case DEVICE_AVAILABILITY_CHANGED:
444 case DEVICE_REMOVED:
445 case DEVICE_SUSPENDED:
446 // state of our device has changed, check if we need
447 // to re-select
448 initializeLocalState();
449 break;
450 default:
451 break;
452 }
453 }
454 }
455 }
456}