blob: 831a8db77e3d4de1f76210bb72aa447d0cebb29c [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 */
Matteo Scandolocf847b82019-04-26 15:00:00 -070016package org.opencord.aaa.impl;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010017
Shubham Sharma4900ce62019-06-19 14:18:50 +000018import com.google.common.collect.Maps;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010019import org.onlab.packet.ARP;
20import org.onlab.packet.DeserializationException;
21import org.onlab.packet.EthType;
22import org.onlab.packet.Ethernet;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010023import org.onlab.packet.IPv4;
Shubham Sharma4900ce62019-06-19 14:18:50 +000024import org.onlab.packet.Ip4Address;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010025import org.onlab.packet.MacAddress;
26import org.onlab.packet.RADIUS;
27import org.onlab.packet.TpPort;
28import org.onlab.packet.UDP;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010029import 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;
Matteo Scandolocf847b82019-04-26 15:00:00 -070046import org.opencord.aaa.AaaConfig;
47import org.opencord.aaa.RadiusCommunicator;
Gamze Abaka1cfdb192018-10-25 11:39:19 +000048import org.opencord.sadis.BaseInformationService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010049import org.opencord.sadis.SubscriberAndDeviceInformation;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010050import org.slf4j.Logger;
51
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010052import java.net.InetAddress;
53import java.nio.ByteBuffer;
54import java.util.Map;
55import java.util.Set;
56
Shubham Sharma4900ce62019-06-19 14:18:50 +000057import static org.onosproject.net.packet.PacketPriority.CONTROL;
58import static org.slf4j.LoggerFactory.getLogger;
59
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010060/**
61 * Handles communication with the RADIUS server through ports
62 * of the SDN switches.
63 */
64public class PortBasedRadiusCommunicator implements RadiusCommunicator {
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +000065 private static final String SADIS_NOT_RUNNING = "Sadis is not running.";
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010066
67 // for verbose output
68 private final Logger log = getLogger(getClass());
69
70 // our unique identifier
71 private ApplicationId appId;
72
73 // to receive Packet-in events that we'll respond to
74 PacketService packetService;
75
76 DeviceService deviceService;
77
78 MastershipService mastershipService;
79
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +000080 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010081
82 // to store local mapping of IP Address and Serial No of Device
83 private Map<Ip4Address, String> ipToSnMap;
84
85 // connect points to the RADIUS server
86 Set<ConnectPoint> radiusConnectPoints;
87
88 // Parsed RADIUS server addresses
89 protected InetAddress radiusIpAddress;
90
91 // RADIUS server TCP port number
92 protected short radiusServerPort;
93
94 protected String radiusMacAddress;
95
96 // NAS IP address
97 protected InetAddress nasIpAddress;
98
99 protected String nasMacAddress;
100
101 // RADIUS server Vlan ID
102 private short radiusVlanID;
103
104 // RADIUS p-bit
105 private byte radiusPBit;
106
107 PacketCustomizer pktCustomizer;
108 AaaManager aaaManager;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100109 ConnectPoint radiusServerConnectPoint = null;
110
111 InnerMastershipListener changeListener = new InnerMastershipListener();
112 InnerDeviceListener deviceListener = new InnerDeviceListener();
113
114 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
115 MastershipService masService, DeviceService devService,
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000116 BaseInformationService<SubscriberAndDeviceInformation> subsService,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100117 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
118 this.appId = appId;
119 this.packetService = pktService;
120 this.mastershipService = masService;
121 this.deviceService = devService;
122 this.subsService = subsService;
123 this.pktCustomizer = pktCustomizer;
124 this.aaaManager = aaaManager;
125
126 ipToSnMap = Maps.newConcurrentMap();
127 mastershipService.addListener(changeListener);
128 deviceService.addListener(deviceListener);
129
Amit Ghoshcf39d972017-08-16 08:15:20 +0100130 log.info("Created PortBased");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100131 }
132
133 private void initializeLocalState() {
134 synchronized (this) {
135 radiusServerConnectPoint = null;
136 if (radiusConnectPoints != null) {
137 // find a connect point through a device for which we are master
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +0000138 for (ConnectPoint cp : radiusConnectPoints) {
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100139 if (mastershipService.isLocalMaster(cp.deviceId())) {
140 if (deviceService.isAvailable(cp.deviceId())) {
141 radiusServerConnectPoint = cp;
142 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100143 break;
144 }
145 }
146 }
147
Amit Ghoshcf39d972017-08-16 08:15:20 +0100148 log.info("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100149
150 if (radiusServerConnectPoint == null) {
151 log.error("Master of none, can't send radius Message to server");
152 }
153 }
154 }
155
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +0000156 public void updateSubsService(BaseInformationService<SubscriberAndDeviceInformation> subsService) {
157 this.subsService = subsService;
158 }
159
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100160 @Override
161 public void initializeLocalState(AaaConfig newCfg) {
162 if (newCfg.nasIp() != null) {
163 nasIpAddress = newCfg.nasIp();
164 }
165 if (newCfg.radiusIp() != null) {
166 radiusIpAddress = newCfg.radiusIp();
167 }
168 if (newCfg.radiusMac() != null) {
169 radiusMacAddress = newCfg.radiusMac();
170 }
171 if (newCfg.nasMac() != null) {
172 nasMacAddress = newCfg.nasMac();
173 }
174
175 radiusServerPort = newCfg.radiusServerUdpPort();
176 radiusVlanID = newCfg.radiusServerVlanId();
177 radiusPBit = newCfg.radiusServerPBit();
178
179 radiusConnectPoints = newCfg.radiusServerConnectPoints();
180
181 initializeLocalState();
182 }
183
184 @Override
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530185 public void clearLocalState() {
186 mastershipService.removeListener(changeListener);
187 deviceService.removeListener(deviceListener);
188 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100189
190 @Override
191 public void deactivate() {
192 mastershipService.removeListener(changeListener);
193 deviceService.removeListener(deviceListener);
194 }
195
196 @Override
197 public void requestIntercepts() {
198 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
199 .matchEthType(Ethernet.TYPE_ARP);
200 packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
201
202 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
203 .matchEthType(Ethernet.TYPE_IPV4)
204 .matchIPProtocol(IPv4.PROTOCOL_UDP)
205 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
206 packetService.requestPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530207
208 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
209 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
210 packetService.requestPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100211 }
212
213 @Override
214 public void withdrawIntercepts() {
215 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
216 .matchEthType(Ethernet.TYPE_ARP);
217 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
218
219 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
220 .matchEthType(Ethernet.TYPE_IPV4)
221 .matchIPProtocol(IPv4.PROTOCOL_UDP)
222 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
223 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530224
225 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
226 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
227 packetService.cancelPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100228 }
229
230 @Override
231 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
232 // create the packet
233 Ethernet ethReply = new Ethernet();
234 ethReply.setSourceMACAddress(nasMacAddress);
235 ethReply.setDestinationMACAddress(radiusMacAddress);
236 ethReply.setEtherType(Ethernet.TYPE_IPV4);
237 ethReply.setVlanID(radiusVlanID);
238 ethReply.setPriorityCode(radiusPBit);
239
240 IPv4 ipv4Packet = new IPv4();
241 ipv4Packet.setTtl((byte) 64);
242 ipv4Packet.setSourceAddress(Ip4Address.
243 valueOf(nasIpAddress).toInt());
244 ipv4Packet.setDestinationAddress(Ip4Address.
245 valueOf(radiusIpAddress).toInt());
246
247 UDP udpPacket = new UDP();
248 udpPacket.setSourcePort(radiusServerPort);
249 udpPacket.setDestinationPort(radiusServerPort);
250
251 udpPacket.setPayload(radiusPacket);
252 ipv4Packet.setPayload(udpPacket);
253 ethReply.setPayload(ipv4Packet);
254
255 // store the IP address and SN of the device, later to be used
256 // for ARP responses
257 String serialNo = deviceService.getDevice(inPkt.
258 receivedFrom().deviceId()).serialNumber();
259
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +0000260 if (subsService == null) {
261 log.warn(SADIS_NOT_RUNNING);
262 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
263 return;
264 }
265
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100266 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
267
268 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100269 log.warn("No Device found with SN {}", serialNo);
Shubham Sharma4900ce62019-06-19 14:18:50 +0000270 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100271 return;
272 }
dvaddire2ae58382017-10-29 08:57:42 +0530273
Shubham Sharma4900ce62019-06-19 14:18:50 +0000274 if (radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_STATUS_REQUEST ||
275 radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_FAKE_ACCESS_REQUEST) {
276 aaaManager.radiusOperationalStatusService.setOutTimeInMillis(radiusPacket.getIdentifier());
277 } else {
278 aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
279 }
dvaddire2ae58382017-10-29 08:57:42 +0530280
281 Ip4Address ipAddress = deviceInfo.ipAddress();
282 if (ipAddress != null) {
283 ipToSnMap.put(ipAddress, serialNo);
284 } else {
285 log.warn("Cannot Map IpAddress to SerialNo : ipAddress = {}", ipAddress);
286 }
287
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100288 // send the message out
289 sendFromRadiusServerPort(pktCustomizer.
290 customizeEthernetIPHeaders(ethReply, inPkt));
Shubham Sharma4900ce62019-06-19 14:18:50 +0000291 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(true);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100292 }
293
294 /**
295 * Sends packet to the RADIUS server using one of the switch ports.
296 *
297 * @param packet Ethernet packet to be sent
298 */
299 private void sendFromRadiusServerPort(Ethernet packet) {
300 if (radiusServerConnectPoint != null) {
301 log.trace("AAA Manager sending Ethernet packet = {}", packet);
302 TrafficTreatment t = DefaultTrafficTreatment.builder()
303 .setOutput(radiusServerConnectPoint.port()).build();
304 OutboundPacket o = new DefaultOutboundPacket(
305 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
306 packetService.emit(o);
307 } else {
308 log.error("Unable to send RADIUS packet, connectPoint is null");
309 }
310 }
311
312 @Override
313 public void handlePacketFromServer(PacketContext context) {
314 // Extract the original Ethernet frame from the packet information
315 InboundPacket pkt = context.inPacket();
316 Ethernet ethPkt = pkt.parsed();
317 if (ethPkt == null) {
318 return;
319 }
320
321 // identify if incoming packet
322 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
323 case ARP:
324 handleArpPacketFromServer(context);
325 break;
326 case IPV4:
327 handleIPv4PacketFromServer(context);
328 break;
329 default:
330 log.debug("Skipping Ethernet packet type {}",
331 EthType.EtherType.lookup(ethPkt.getEtherType()));
332 }
333 }
334
335 /**
336 * Handles ARP packets from RADIUS server.
337 *
338 * @param context Context for the packet
339 */
340 private void handleArpPacketFromServer(PacketContext context) {
341 // Extract the original Ethernet frame from the packet information
342 InboundPacket pkt = context.inPacket();
343 Ethernet ethPkt = pkt.parsed();
344 if (ethPkt == null) {
345 return;
346 }
347
348 ARP arpPacket = (ARP) ethPkt.getPayload();
349
350 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
351 getTargetProtocolAddress());
352
353 String serialNo = ipToSnMap.get(targetAddress);
354 if (serialNo == null) {
355 log.info("No mapping found for ARP reply, target address {}",
356 targetAddress);
357 return;
358 }
Ilayda Ozdemir9fdeee72021-02-26 12:24:27 +0000359
360 if (subsService == null) {
361 log.warn(SADIS_NOT_RUNNING);
362 return;
363 }
364
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100365 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
366 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100367 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100368 return;
369 }
370
371 ARP arpReply = (ARP) arpPacket.clone();
372 arpReply.setOpCode(ARP.OP_REPLY);
373 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
374 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
375 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
376 arpReply.setSenderHardwareAddress(senderMac.toBytes());
377
378 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
379
380 // Ethernet Frame.
381 Ethernet ethReply = new Ethernet();
382 ethReply.setSourceMACAddress(senderMac);
383 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
384 ethReply.setEtherType(Ethernet.TYPE_ARP);
385 ethReply.setVlanID(radiusVlanID);
386 ethReply.setPriorityCode(ethPkt.getPriorityCode());
387
388 ethReply.setPayload(arpReply);
389 sendFromRadiusServerPort(ethReply);
390 }
391
392 /**
393 * Handles IP packets from RADIUS server.
394 *
395 * @param context Context for the packet
396 */
397 private void handleIPv4PacketFromServer(PacketContext context) {
398 // Extract the original Ethernet frame from the packet information
399 InboundPacket pkt = context.inPacket();
400 Ethernet ethPkt = pkt.parsed();
401 if (ethPkt == null) {
402 return;
403 }
404
405 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
406
407 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
408 UDP udpPacket = (UDP) ipv4Packet.getPayload();
409
410 if (udpPacket.getSourcePort() == radiusServerPort) {
411 //This packet is RADIUS packet from the server.
412 RADIUS radiusMsg;
413 try {
414 radiusMsg =
415 RADIUS.deserializer()
416 .deserialize(udpPacket.serialize(),
417 8,
418 udpPacket.getLength() - 8);
Jonathan Hart612651f2019-11-25 09:21:43 -0800419 aaaManager.aaaStatisticsManager.handleRoundtripTime(radiusMsg.getIdentifier());
420 aaaManager.handleRadiusPacket(radiusMsg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100421 } catch (DeserializationException dex) {
422 log.error("Cannot deserialize packet", dex);
423 }
424 }
425 }
426 }
427
428 /**
429 * Handles Mastership changes for the devices which connect
430 * to the RADIUS server.
431 */
432 private class InnerMastershipListener implements MastershipListener {
433 @Override
434 public void event(MastershipEvent event) {
435 if (radiusServerConnectPoint != null &&
436 radiusServerConnectPoint.deviceId().
437 equals(event.subject())) {
438 log.trace("Mastership Event recevived for {}", event.subject());
439 // mastership of the device for our connect point has changed
440 // reselect
441 initializeLocalState();
442 }
443 }
444 }
445
446 /**
447 * Handles Device status change for the devices which connect
448 * to the RADIUS server.
449 */
450 private class InnerDeviceListener implements DeviceListener {
451 @Override
452 public void event(DeviceEvent event) {
453 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
454 if (radiusServerConnectPoint == null) {
455 switch (event.type()) {
456 case DEVICE_ADDED:
457 case DEVICE_AVAILABILITY_CHANGED:
458 // some device is available check if we can get one
459 initializeLocalState();
460 break;
461 default:
462 break;
463 }
464 return;
465 }
466 if (radiusServerConnectPoint.deviceId().
467 equals(event.subject().id())) {
468 switch (event.type()) {
469 case DEVICE_AVAILABILITY_CHANGED:
470 case DEVICE_REMOVED:
471 case DEVICE_SUSPENDED:
472 // state of our device has changed, check if we need
473 // to re-select
474 initializeLocalState();
475 break;
476 default:
477 break;
478 }
479 }
480 }
481 }
482}