blob: 92fea87df46e7ba78a537dab56d97ddef48ff866 [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
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
Matteo Scandolocf847b82019-04-26 15:00:00 -070047import org.opencord.aaa.AaaConfig;
48import org.opencord.aaa.RadiusCommunicator;
Gamze Abaka1cfdb192018-10-25 11:39:19 +000049import org.opencord.sadis.BaseInformationService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010050import org.opencord.sadis.SubscriberAndDeviceInformation;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010051
52import org.slf4j.Logger;
53
54import com.google.common.collect.Maps;
55
56import static org.onosproject.net.packet.PacketPriority.CONTROL;
57import static org.slf4j.LoggerFactory.getLogger;
58
59import java.net.InetAddress;
60import java.nio.ByteBuffer;
61import java.util.Map;
62import java.util.Set;
63
64/**
65 * Handles communication with the RADIUS server through ports
66 * of the SDN switches.
67 */
68public class PortBasedRadiusCommunicator implements RadiusCommunicator {
69
70 // for verbose output
71 private final Logger log = getLogger(getClass());
72
73 // our unique identifier
74 private ApplicationId appId;
75
76 // to receive Packet-in events that we'll respond to
77 PacketService packetService;
78
79 DeviceService deviceService;
80
81 MastershipService mastershipService;
82
Gamze Abaka1cfdb192018-10-25 11:39:19 +000083 BaseInformationService<SubscriberAndDeviceInformation> subsService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010084
85 // to store local mapping of IP Address and Serial No of Device
86 private Map<Ip4Address, String> ipToSnMap;
87
88 // connect points to the RADIUS server
89 Set<ConnectPoint> radiusConnectPoints;
90
91 // Parsed RADIUS server addresses
92 protected InetAddress radiusIpAddress;
93
94 // RADIUS server TCP port number
95 protected short radiusServerPort;
96
97 protected String radiusMacAddress;
98
99 // NAS IP address
100 protected InetAddress nasIpAddress;
101
102 protected String nasMacAddress;
103
104 // RADIUS server Vlan ID
105 private short radiusVlanID;
106
107 // RADIUS p-bit
108 private byte radiusPBit;
109
110 PacketCustomizer pktCustomizer;
111 AaaManager aaaManager;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100112 ConnectPoint radiusServerConnectPoint = null;
113
114 InnerMastershipListener changeListener = new InnerMastershipListener();
115 InnerDeviceListener deviceListener = new InnerDeviceListener();
116
117 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
118 MastershipService masService, DeviceService devService,
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000119 BaseInformationService<SubscriberAndDeviceInformation> subsService,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100120 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
121 this.appId = appId;
122 this.packetService = pktService;
123 this.mastershipService = masService;
124 this.deviceService = devService;
125 this.subsService = subsService;
126 this.pktCustomizer = pktCustomizer;
127 this.aaaManager = aaaManager;
128
129 ipToSnMap = Maps.newConcurrentMap();
130 mastershipService.addListener(changeListener);
131 deviceService.addListener(deviceListener);
132
Amit Ghoshcf39d972017-08-16 08:15:20 +0100133 log.info("Created PortBased");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100134 }
135
136 private void initializeLocalState() {
137 synchronized (this) {
138 radiusServerConnectPoint = null;
139 if (radiusConnectPoints != null) {
140 // find a connect point through a device for which we are master
141 for (ConnectPoint cp: radiusConnectPoints) {
142 if (mastershipService.isLocalMaster(cp.deviceId())) {
143 if (deviceService.isAvailable(cp.deviceId())) {
144 radiusServerConnectPoint = cp;
145 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100146 break;
147 }
148 }
149 }
150
Amit Ghoshcf39d972017-08-16 08:15:20 +0100151 log.info("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100152
153 if (radiusServerConnectPoint == null) {
154 log.error("Master of none, can't send radius Message to server");
155 }
156 }
157 }
158
159 @Override
160 public void initializeLocalState(AaaConfig newCfg) {
161 if (newCfg.nasIp() != null) {
162 nasIpAddress = newCfg.nasIp();
163 }
164 if (newCfg.radiusIp() != null) {
165 radiusIpAddress = newCfg.radiusIp();
166 }
167 if (newCfg.radiusMac() != null) {
168 radiusMacAddress = newCfg.radiusMac();
169 }
170 if (newCfg.nasMac() != null) {
171 nasMacAddress = newCfg.nasMac();
172 }
173
174 radiusServerPort = newCfg.radiusServerUdpPort();
175 radiusVlanID = newCfg.radiusServerVlanId();
176 radiusPBit = newCfg.radiusServerPBit();
177
178 radiusConnectPoints = newCfg.radiusServerConnectPoints();
179
180 initializeLocalState();
181 }
182
183 @Override
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530184 public void clearLocalState() {
185 mastershipService.removeListener(changeListener);
186 deviceService.removeListener(deviceListener);
187 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100188
189 @Override
190 public void deactivate() {
191 mastershipService.removeListener(changeListener);
192 deviceService.removeListener(deviceListener);
193 }
194
195 @Override
196 public void requestIntercepts() {
197 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
198 .matchEthType(Ethernet.TYPE_ARP);
199 packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
200
201 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
202 .matchEthType(Ethernet.TYPE_IPV4)
203 .matchIPProtocol(IPv4.PROTOCOL_UDP)
204 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
205 packetService.requestPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530206
207 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
208 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
209 packetService.requestPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100210 }
211
212 @Override
213 public void withdrawIntercepts() {
214 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
215 .matchEthType(Ethernet.TYPE_ARP);
216 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
217
218 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
219 .matchEthType(Ethernet.TYPE_IPV4)
220 .matchIPProtocol(IPv4.PROTOCOL_UDP)
221 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
222 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
Deepa Vaddireddye0e10722017-09-27 05:00:10 +0530223
224 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
225 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
226 packetService.cancelPackets(selector.build(), CONTROL, appId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100227 }
228
229 @Override
230 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
231 // create the packet
232 Ethernet ethReply = new Ethernet();
233 ethReply.setSourceMACAddress(nasMacAddress);
234 ethReply.setDestinationMACAddress(radiusMacAddress);
235 ethReply.setEtherType(Ethernet.TYPE_IPV4);
236 ethReply.setVlanID(radiusVlanID);
237 ethReply.setPriorityCode(radiusPBit);
238
239 IPv4 ipv4Packet = new IPv4();
240 ipv4Packet.setTtl((byte) 64);
241 ipv4Packet.setSourceAddress(Ip4Address.
242 valueOf(nasIpAddress).toInt());
243 ipv4Packet.setDestinationAddress(Ip4Address.
244 valueOf(radiusIpAddress).toInt());
245
246 UDP udpPacket = new UDP();
247 udpPacket.setSourcePort(radiusServerPort);
248 udpPacket.setDestinationPort(radiusServerPort);
249
250 udpPacket.setPayload(radiusPacket);
251 ipv4Packet.setPayload(udpPacket);
252 ethReply.setPayload(ipv4Packet);
253
254 // store the IP address and SN of the device, later to be used
255 // for ARP responses
256 String serialNo = deviceService.getDevice(inPkt.
257 receivedFrom().deviceId()).serialNumber();
258
259 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
260
261 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100262 log.warn("No Device found with SN {}", serialNo);
Shubham Sharma048cc262019-06-19 14:18:50 +0000263 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100264 return;
265 }
266 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
Shubham Sharma048cc262019-06-19 14:18:50 +0000267 if (radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_STATUS_REQUEST ||
268 radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_FAKE_ACCESS_REQUEST) {
269 aaaManager.radiusOperationalStatusService.setOutTimeInMillis(radiusPacket.getIdentifier());
270 } else {
271 aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
272 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100273 // send the message out
274 sendFromRadiusServerPort(pktCustomizer.
275 customizeEthernetIPHeaders(ethReply, inPkt));
Shubham Sharma048cc262019-06-19 14:18:50 +0000276 aaaManager.radiusOperationalStatusService.setStatusServerReqSent(true);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100277 }
278
279 /**
280 * Sends packet to the RADIUS server using one of the switch ports.
281 *
282 * @param packet Ethernet packet to be sent
283 */
284 private void sendFromRadiusServerPort(Ethernet packet) {
285 if (radiusServerConnectPoint != null) {
286 log.trace("AAA Manager sending Ethernet packet = {}", packet);
287 TrafficTreatment t = DefaultTrafficTreatment.builder()
288 .setOutput(radiusServerConnectPoint.port()).build();
289 OutboundPacket o = new DefaultOutboundPacket(
290 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
291 packetService.emit(o);
292 } else {
293 log.error("Unable to send RADIUS packet, connectPoint is null");
294 }
295 }
296
297 @Override
298 public void handlePacketFromServer(PacketContext context) {
299 // Extract the original Ethernet frame from the packet information
300 InboundPacket pkt = context.inPacket();
301 Ethernet ethPkt = pkt.parsed();
302 if (ethPkt == null) {
303 return;
304 }
305
306 // identify if incoming packet
307 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
308 case ARP:
309 handleArpPacketFromServer(context);
310 break;
311 case IPV4:
312 handleIPv4PacketFromServer(context);
313 break;
314 default:
315 log.debug("Skipping Ethernet packet type {}",
316 EthType.EtherType.lookup(ethPkt.getEtherType()));
317 }
318 }
319
320 /**
321 * Handles ARP packets from RADIUS server.
322 *
323 * @param context Context for the packet
324 */
325 private void handleArpPacketFromServer(PacketContext context) {
326 // Extract the original Ethernet frame from the packet information
327 InboundPacket pkt = context.inPacket();
328 Ethernet ethPkt = pkt.parsed();
329 if (ethPkt == null) {
330 return;
331 }
332
333 ARP arpPacket = (ARP) ethPkt.getPayload();
334
335 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
336 getTargetProtocolAddress());
337
338 String serialNo = ipToSnMap.get(targetAddress);
339 if (serialNo == null) {
340 log.info("No mapping found for ARP reply, target address {}",
341 targetAddress);
342 return;
343 }
344 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
345 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100346 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100347 return;
348 }
349
350 ARP arpReply = (ARP) arpPacket.clone();
351 arpReply.setOpCode(ARP.OP_REPLY);
352 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
353 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
354 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
355 arpReply.setSenderHardwareAddress(senderMac.toBytes());
356
357 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
358
359 // Ethernet Frame.
360 Ethernet ethReply = new Ethernet();
361 ethReply.setSourceMACAddress(senderMac);
362 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
363 ethReply.setEtherType(Ethernet.TYPE_ARP);
364 ethReply.setVlanID(radiusVlanID);
365 ethReply.setPriorityCode(ethPkt.getPriorityCode());
366
367 ethReply.setPayload(arpReply);
368 sendFromRadiusServerPort(ethReply);
369 }
370
371 /**
372 * Handles IP packets from RADIUS server.
373 *
374 * @param context Context for the packet
375 */
376 private void handleIPv4PacketFromServer(PacketContext context) {
377 // Extract the original Ethernet frame from the packet information
378 InboundPacket pkt = context.inPacket();
379 Ethernet ethPkt = pkt.parsed();
380 if (ethPkt == null) {
381 return;
382 }
383
384 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
385
386 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
387 UDP udpPacket = (UDP) ipv4Packet.getPayload();
388
389 if (udpPacket.getSourcePort() == radiusServerPort) {
390 //This packet is RADIUS packet from the server.
391 RADIUS radiusMsg;
392 try {
393 radiusMsg =
394 RADIUS.deserializer()
395 .deserialize(udpPacket.serialize(),
396 8,
397 udpPacket.getLength() - 8);
398 try {
kartikey dubeye1545422019-05-22 12:53:45 +0000399 aaaManager.aaaStatisticsManager.handleRoundtripTime(radiusMsg.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100400 aaaManager.handleRadiusPacket(radiusMsg);
401 } catch (StateMachineException sme) {
402 log.error("Illegal state machine operation", sme);
403 }
404 } 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}