blob: 8973a5f5cb4b38c477d25f746e3b2fdea9bd099d [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 {
65
66 // for verbose output
67 private final Logger log = getLogger(getClass());
68
69 // our unique identifier
70 private ApplicationId appId;
71
72 // to receive Packet-in events that we'll respond to
73 PacketService packetService;
74
75 DeviceService deviceService;
76
77 MastershipService mastershipService;
78
Gamze Abaka1cfdb192018-10-25 11:39:19 +000079 BaseInformationService<SubscriberAndDeviceInformation> subsService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010080
81 // to store local mapping of IP Address and Serial No of Device
82 private Map<Ip4Address, String> ipToSnMap;
83
84 // connect points to the RADIUS server
85 Set<ConnectPoint> radiusConnectPoints;
86
87 // Parsed RADIUS server addresses
88 protected InetAddress radiusIpAddress;
89
90 // RADIUS server TCP port number
91 protected short radiusServerPort;
92
93 protected String radiusMacAddress;
94
95 // NAS IP address
96 protected InetAddress nasIpAddress;
97
98 protected String nasMacAddress;
99
100 // RADIUS server Vlan ID
101 private short radiusVlanID;
102
103 // RADIUS p-bit
104 private byte radiusPBit;
105
106 PacketCustomizer pktCustomizer;
107 AaaManager aaaManager;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100108 ConnectPoint radiusServerConnectPoint = null;
109
110 InnerMastershipListener changeListener = new InnerMastershipListener();
111 InnerDeviceListener deviceListener = new InnerDeviceListener();
112
113 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
114 MastershipService masService, DeviceService devService,
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000115 BaseInformationService<SubscriberAndDeviceInformation> subsService,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100116 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
117 this.appId = appId;
118 this.packetService = pktService;
119 this.mastershipService = masService;
120 this.deviceService = devService;
121 this.subsService = subsService;
122 this.pktCustomizer = pktCustomizer;
123 this.aaaManager = aaaManager;
124
125 ipToSnMap = Maps.newConcurrentMap();
126 mastershipService.addListener(changeListener);
127 deviceService.addListener(deviceListener);
128
Amit Ghoshcf39d972017-08-16 08:15:20 +0100129 log.info("Created PortBased");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100130 }
131
132 private void initializeLocalState() {
133 synchronized (this) {
134 radiusServerConnectPoint = null;
135 if (radiusConnectPoints != null) {
136 // find a connect point through a device for which we are master
137 for (ConnectPoint cp: radiusConnectPoints) {
138 if (mastershipService.isLocalMaster(cp.deviceId())) {
139 if (deviceService.isAvailable(cp.deviceId())) {
140 radiusServerConnectPoint = cp;
141 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100142 break;
143 }
144 }
145 }
146
Amit Ghoshcf39d972017-08-16 08:15:20 +0100147 log.info("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100148
149 if (radiusServerConnectPoint == null) {
150 log.error("Master of none, can't send radius Message to server");
151 }
152 }
153 }
154
155 @Override
156 public void initializeLocalState(AaaConfig newCfg) {
157 if (newCfg.nasIp() != null) {
158 nasIpAddress = newCfg.nasIp();
159 }
160 if (newCfg.radiusIp() != null) {
161 radiusIpAddress = newCfg.radiusIp();
162 }
163 if (newCfg.radiusMac() != null) {
164 radiusMacAddress = newCfg.radiusMac();
165 }
166 if (newCfg.nasMac() != null) {
167 nasMacAddress = newCfg.nasMac();
168 }
169
170 radiusServerPort = newCfg.radiusServerUdpPort();
171 radiusVlanID = newCfg.radiusServerVlanId();
172 radiusPBit = newCfg.radiusServerPBit();
173
174 radiusConnectPoints = newCfg.radiusServerConnectPoints();
175
176 initializeLocalState();
177 }
178
179 @Override
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530180 public void clearLocalState() {
181 mastershipService.removeListener(changeListener);
182 deviceService.removeListener(deviceListener);
183 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100184
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);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530202
203 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
204 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
205 packetService.requestPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100206 }
207
208 @Override
209 public void withdrawIntercepts() {
210 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
211 .matchEthType(Ethernet.TYPE_ARP);
212 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
213
214 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
215 .matchEthType(Ethernet.TYPE_IPV4)
216 .matchIPProtocol(IPv4.PROTOCOL_UDP)
217 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
218 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530219
220 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
221 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
222 packetService.cancelPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100223 }
224
225 @Override
226 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
227 // create the packet
228 Ethernet ethReply = new Ethernet();
229 ethReply.setSourceMACAddress(nasMacAddress);
230 ethReply.setDestinationMACAddress(radiusMacAddress);
231 ethReply.setEtherType(Ethernet.TYPE_IPV4);
232 ethReply.setVlanID(radiusVlanID);
233 ethReply.setPriorityCode(radiusPBit);
234
235 IPv4 ipv4Packet = new IPv4();
236 ipv4Packet.setTtl((byte) 64);
237 ipv4Packet.setSourceAddress(Ip4Address.
238 valueOf(nasIpAddress).toInt());
239 ipv4Packet.setDestinationAddress(Ip4Address.
240 valueOf(radiusIpAddress).toInt());
241
242 UDP udpPacket = new UDP();
243 udpPacket.setSourcePort(radiusServerPort);
244 udpPacket.setDestinationPort(radiusServerPort);
245
246 udpPacket.setPayload(radiusPacket);
247 ipv4Packet.setPayload(udpPacket);
248 ethReply.setPayload(ipv4Packet);
249
250 // store the IP address and SN of the device, later to be used
251 // for ARP responses
252 String serialNo = deviceService.getDevice(inPkt.
253 receivedFrom().deviceId()).serialNumber();
254
255 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
256
257 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100258 log.warn("No Device found with SN {}", serialNo);
Shubham Sharma4900ce62019-06-19 14:18:50 +0000259 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100260 return;
261 }
dvaddire2ae58382017-10-29 08:57:42 +0530262
Shubham Sharma4900ce62019-06-19 14:18:50 +0000263 if (radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_STATUS_REQUEST ||
264 radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_FAKE_ACCESS_REQUEST) {
265 aaaManager.radiusOperationalStatusService.setOutTimeInMillis(radiusPacket.getIdentifier());
266 } else {
267 aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
268 }
dvaddire2ae58382017-10-29 08:57:42 +0530269
270 Ip4Address ipAddress = deviceInfo.ipAddress();
271 if (ipAddress != null) {
272 ipToSnMap.put(ipAddress, serialNo);
273 } else {
274 log.warn("Cannot Map IpAddress to SerialNo : ipAddress = {}", ipAddress);
275 }
276
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100277 // send the message out
278 sendFromRadiusServerPort(pktCustomizer.
279 customizeEthernetIPHeaders(ethReply, inPkt));
Shubham Sharma4900ce62019-06-19 14:18:50 +0000280 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(true);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100281 }
282
283 /**
284 * Sends packet to the RADIUS server using one of the switch ports.
285 *
286 * @param packet Ethernet packet to be sent
287 */
288 private void sendFromRadiusServerPort(Ethernet packet) {
289 if (radiusServerConnectPoint != null) {
290 log.trace("AAA Manager sending Ethernet packet = {}", packet);
291 TrafficTreatment t = DefaultTrafficTreatment.builder()
292 .setOutput(radiusServerConnectPoint.port()).build();
293 OutboundPacket o = new DefaultOutboundPacket(
294 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
295 packetService.emit(o);
296 } else {
297 log.error("Unable to send RADIUS packet, connectPoint is null");
298 }
299 }
300
301 @Override
302 public void handlePacketFromServer(PacketContext context) {
303 // Extract the original Ethernet frame from the packet information
304 InboundPacket pkt = context.inPacket();
305 Ethernet ethPkt = pkt.parsed();
306 if (ethPkt == null) {
307 return;
308 }
309
310 // identify if incoming packet
311 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
312 case ARP:
313 handleArpPacketFromServer(context);
314 break;
315 case IPV4:
316 handleIPv4PacketFromServer(context);
317 break;
318 default:
319 log.debug("Skipping Ethernet packet type {}",
320 EthType.EtherType.lookup(ethPkt.getEtherType()));
321 }
322 }
323
324 /**
325 * Handles ARP packets from RADIUS server.
326 *
327 * @param context Context for the packet
328 */
329 private void handleArpPacketFromServer(PacketContext context) {
330 // Extract the original Ethernet frame from the packet information
331 InboundPacket pkt = context.inPacket();
332 Ethernet ethPkt = pkt.parsed();
333 if (ethPkt == null) {
334 return;
335 }
336
337 ARP arpPacket = (ARP) ethPkt.getPayload();
338
339 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
340 getTargetProtocolAddress());
341
342 String serialNo = ipToSnMap.get(targetAddress);
343 if (serialNo == null) {
344 log.info("No mapping found for ARP reply, target address {}",
345 targetAddress);
346 return;
347 }
348 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
349 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100350 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100351 return;
352 }
353
354 ARP arpReply = (ARP) arpPacket.clone();
355 arpReply.setOpCode(ARP.OP_REPLY);
356 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
357 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
358 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
359 arpReply.setSenderHardwareAddress(senderMac.toBytes());
360
361 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
362
363 // Ethernet Frame.
364 Ethernet ethReply = new Ethernet();
365 ethReply.setSourceMACAddress(senderMac);
366 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
367 ethReply.setEtherType(Ethernet.TYPE_ARP);
368 ethReply.setVlanID(radiusVlanID);
369 ethReply.setPriorityCode(ethPkt.getPriorityCode());
370
371 ethReply.setPayload(arpReply);
372 sendFromRadiusServerPort(ethReply);
373 }
374
375 /**
376 * Handles IP packets from RADIUS server.
377 *
378 * @param context Context for the packet
379 */
380 private void handleIPv4PacketFromServer(PacketContext context) {
381 // Extract the original Ethernet frame from the packet information
382 InboundPacket pkt = context.inPacket();
383 Ethernet ethPkt = pkt.parsed();
384 if (ethPkt == null) {
385 return;
386 }
387
388 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
389
390 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
391 UDP udpPacket = (UDP) ipv4Packet.getPayload();
392
393 if (udpPacket.getSourcePort() == radiusServerPort) {
394 //This packet is RADIUS packet from the server.
395 RADIUS radiusMsg;
396 try {
397 radiusMsg =
398 RADIUS.deserializer()
399 .deserialize(udpPacket.serialize(),
400 8,
401 udpPacket.getLength() - 8);
Jonathan Hart612651f2019-11-25 09:21:43 -0800402 aaaManager.aaaStatisticsManager.handleRoundtripTime(radiusMsg.getIdentifier());
403 aaaManager.handleRadiusPacket(radiusMsg);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100404 } catch (DeserializationException dex) {
405 log.error("Cannot deserialize packet", dex);
406 }
407 }
408 }
409 }
410
411 /**
412 * Handles Mastership changes for the devices which connect
413 * to the RADIUS server.
414 */
415 private class InnerMastershipListener implements MastershipListener {
416 @Override
417 public void event(MastershipEvent event) {
418 if (radiusServerConnectPoint != null &&
419 radiusServerConnectPoint.deviceId().
420 equals(event.subject())) {
421 log.trace("Mastership Event recevived for {}", event.subject());
422 // mastership of the device for our connect point has changed
423 // reselect
424 initializeLocalState();
425 }
426 }
427 }
428
429 /**
430 * Handles Device status change for the devices which connect
431 * to the RADIUS server.
432 */
433 private class InnerDeviceListener implements DeviceListener {
434 @Override
435 public void event(DeviceEvent event) {
436 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
437 if (radiusServerConnectPoint == null) {
438 switch (event.type()) {
439 case DEVICE_ADDED:
440 case DEVICE_AVAILABILITY_CHANGED:
441 // some device is available check if we can get one
442 initializeLocalState();
443 break;
444 default:
445 break;
446 }
447 return;
448 }
449 if (radiusServerConnectPoint.deviceId().
450 equals(event.subject().id())) {
451 switch (event.type()) {
452 case DEVICE_AVAILABILITY_CHANGED:
453 case DEVICE_REMOVED:
454 case DEVICE_SUSPENDED:
455 // state of our device has changed, check if we need
456 // to re-select
457 initializeLocalState();
458 break;
459 default:
460 break;
461 }
462 }
463 }
464 }
465}