blob: 8e93777b7455f9dd1184d2d6ceb5ec6534a3e306 [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
183 public void clearLocalState() {}
184
185 @Override
186 public void deactivate() {
187 mastershipService.removeListener(changeListener);
188 deviceService.removeListener(deviceListener);
189 }
190
191 @Override
192 public void requestIntercepts() {
193 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
194 .matchEthType(Ethernet.TYPE_ARP);
195 packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
196
197 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
198 .matchEthType(Ethernet.TYPE_IPV4)
199 .matchIPProtocol(IPv4.PROTOCOL_UDP)
200 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
201 packetService.requestPackets(selectorServer.build(), CONTROL, appId);
202 }
203
204 @Override
205 public void withdrawIntercepts() {
206 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
207 .matchEthType(Ethernet.TYPE_ARP);
208 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
209
210 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
211 .matchEthType(Ethernet.TYPE_IPV4)
212 .matchIPProtocol(IPv4.PROTOCOL_UDP)
213 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
214 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
215 }
216
217 @Override
218 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
219 // create the packet
220 Ethernet ethReply = new Ethernet();
221 ethReply.setSourceMACAddress(nasMacAddress);
222 ethReply.setDestinationMACAddress(radiusMacAddress);
223 ethReply.setEtherType(Ethernet.TYPE_IPV4);
224 ethReply.setVlanID(radiusVlanID);
225 ethReply.setPriorityCode(radiusPBit);
226
227 IPv4 ipv4Packet = new IPv4();
228 ipv4Packet.setTtl((byte) 64);
229 ipv4Packet.setSourceAddress(Ip4Address.
230 valueOf(nasIpAddress).toInt());
231 ipv4Packet.setDestinationAddress(Ip4Address.
232 valueOf(radiusIpAddress).toInt());
233
234 UDP udpPacket = new UDP();
235 udpPacket.setSourcePort(radiusServerPort);
236 udpPacket.setDestinationPort(radiusServerPort);
237
238 udpPacket.setPayload(radiusPacket);
239 ipv4Packet.setPayload(udpPacket);
240 ethReply.setPayload(ipv4Packet);
241
242 // store the IP address and SN of the device, later to be used
243 // for ARP responses
244 String serialNo = deviceService.getDevice(inPkt.
245 receivedFrom().deviceId()).serialNumber();
246
247 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
248
249 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100250 log.warn("No Device found with SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100251 return;
252 }
253 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
254
255 // send the message out
256 sendFromRadiusServerPort(pktCustomizer.
257 customizeEthernetIPHeaders(ethReply, inPkt));
258 }
259
260 /**
261 * Sends packet to the RADIUS server using one of the switch ports.
262 *
263 * @param packet Ethernet packet to be sent
264 */
265 private void sendFromRadiusServerPort(Ethernet packet) {
266 if (radiusServerConnectPoint != null) {
267 log.trace("AAA Manager sending Ethernet packet = {}", packet);
268 TrafficTreatment t = DefaultTrafficTreatment.builder()
269 .setOutput(radiusServerConnectPoint.port()).build();
270 OutboundPacket o = new DefaultOutboundPacket(
271 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
272 packetService.emit(o);
273 } else {
274 log.error("Unable to send RADIUS packet, connectPoint is null");
275 }
276 }
277
278 @Override
279 public void handlePacketFromServer(PacketContext context) {
280 // Extract the original Ethernet frame from the packet information
281 InboundPacket pkt = context.inPacket();
282 Ethernet ethPkt = pkt.parsed();
283 if (ethPkt == null) {
284 return;
285 }
286
287 // identify if incoming packet
288 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
289 case ARP:
290 handleArpPacketFromServer(context);
291 break;
292 case IPV4:
293 handleIPv4PacketFromServer(context);
294 break;
295 default:
296 log.debug("Skipping Ethernet packet type {}",
297 EthType.EtherType.lookup(ethPkt.getEtherType()));
298 }
299 }
300
301 /**
302 * Handles ARP packets from RADIUS server.
303 *
304 * @param context Context for the packet
305 */
306 private void handleArpPacketFromServer(PacketContext context) {
307 // Extract the original Ethernet frame from the packet information
308 InboundPacket pkt = context.inPacket();
309 Ethernet ethPkt = pkt.parsed();
310 if (ethPkt == null) {
311 return;
312 }
313
314 ARP arpPacket = (ARP) ethPkt.getPayload();
315
316 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
317 getTargetProtocolAddress());
318
319 String serialNo = ipToSnMap.get(targetAddress);
320 if (serialNo == null) {
321 log.info("No mapping found for ARP reply, target address {}",
322 targetAddress);
323 return;
324 }
325 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
326 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100327 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100328 return;
329 }
330
331 ARP arpReply = (ARP) arpPacket.clone();
332 arpReply.setOpCode(ARP.OP_REPLY);
333 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
334 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
335 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
336 arpReply.setSenderHardwareAddress(senderMac.toBytes());
337
338 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
339
340 // Ethernet Frame.
341 Ethernet ethReply = new Ethernet();
342 ethReply.setSourceMACAddress(senderMac);
343 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
344 ethReply.setEtherType(Ethernet.TYPE_ARP);
345 ethReply.setVlanID(radiusVlanID);
346 ethReply.setPriorityCode(ethPkt.getPriorityCode());
347
348 ethReply.setPayload(arpReply);
349 sendFromRadiusServerPort(ethReply);
350 }
351
352 /**
353 * Handles IP packets from RADIUS server.
354 *
355 * @param context Context for the packet
356 */
357 private void handleIPv4PacketFromServer(PacketContext context) {
358 // Extract the original Ethernet frame from the packet information
359 InboundPacket pkt = context.inPacket();
360 Ethernet ethPkt = pkt.parsed();
361 if (ethPkt == null) {
362 return;
363 }
364
365 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
366
367 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
368 UDP udpPacket = (UDP) ipv4Packet.getPayload();
369
370 if (udpPacket.getSourcePort() == radiusServerPort) {
371 //This packet is RADIUS packet from the server.
372 RADIUS radiusMsg;
373 try {
374 radiusMsg =
375 RADIUS.deserializer()
376 .deserialize(udpPacket.serialize(),
377 8,
378 udpPacket.getLength() - 8);
379 try {
380 aaaManager.handleRadiusPacket(radiusMsg);
381 } catch (StateMachineException sme) {
382 log.error("Illegal state machine operation", sme);
383 }
384 } catch (DeserializationException dex) {
385 log.error("Cannot deserialize packet", dex);
386 }
387 }
388 }
389 }
390
391 /**
392 * Handles Mastership changes for the devices which connect
393 * to the RADIUS server.
394 */
395 private class InnerMastershipListener implements MastershipListener {
396 @Override
397 public void event(MastershipEvent event) {
398 if (radiusServerConnectPoint != null &&
399 radiusServerConnectPoint.deviceId().
400 equals(event.subject())) {
401 log.trace("Mastership Event recevived for {}", event.subject());
402 // mastership of the device for our connect point has changed
403 // reselect
404 initializeLocalState();
405 }
406 }
407 }
408
409 /**
410 * Handles Device status change for the devices which connect
411 * to the RADIUS server.
412 */
413 private class InnerDeviceListener implements DeviceListener {
414 @Override
415 public void event(DeviceEvent event) {
416 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
417 if (radiusServerConnectPoint == null) {
418 switch (event.type()) {
419 case DEVICE_ADDED:
420 case DEVICE_AVAILABILITY_CHANGED:
421 // some device is available check if we can get one
422 initializeLocalState();
423 break;
424 default:
425 break;
426 }
427 return;
428 }
429 if (radiusServerConnectPoint.deviceId().
430 equals(event.subject().id())) {
431 switch (event.type()) {
432 case DEVICE_AVAILABILITY_CHANGED:
433 case DEVICE_REMOVED:
434 case DEVICE_SUSPENDED:
435 // state of our device has changed, check if we need
436 // to re-select
437 initializeLocalState();
438 break;
439 default:
440 break;
441 }
442 }
443 }
444 }
445}