blob: 2967a147baa57cf63b9819560c52670947636256 [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);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100263 return;
264 }
265 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
kartikey dubeye1545422019-05-22 12:53:45 +0000266 aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100267 // send the message out
268 sendFromRadiusServerPort(pktCustomizer.
269 customizeEthernetIPHeaders(ethReply, inPkt));
270 }
271
272 /**
273 * Sends packet to the RADIUS server using one of the switch ports.
274 *
275 * @param packet Ethernet packet to be sent
276 */
277 private void sendFromRadiusServerPort(Ethernet packet) {
278 if (radiusServerConnectPoint != null) {
279 log.trace("AAA Manager sending Ethernet packet = {}", packet);
280 TrafficTreatment t = DefaultTrafficTreatment.builder()
281 .setOutput(radiusServerConnectPoint.port()).build();
282 OutboundPacket o = new DefaultOutboundPacket(
283 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
284 packetService.emit(o);
285 } else {
286 log.error("Unable to send RADIUS packet, connectPoint is null");
287 }
288 }
289
290 @Override
291 public void handlePacketFromServer(PacketContext context) {
292 // Extract the original Ethernet frame from the packet information
293 InboundPacket pkt = context.inPacket();
294 Ethernet ethPkt = pkt.parsed();
295 if (ethPkt == null) {
296 return;
297 }
298
299 // identify if incoming packet
300 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
301 case ARP:
302 handleArpPacketFromServer(context);
303 break;
304 case IPV4:
305 handleIPv4PacketFromServer(context);
306 break;
307 default:
308 log.debug("Skipping Ethernet packet type {}",
309 EthType.EtherType.lookup(ethPkt.getEtherType()));
310 }
311 }
312
313 /**
314 * Handles ARP packets from RADIUS server.
315 *
316 * @param context Context for the packet
317 */
318 private void handleArpPacketFromServer(PacketContext context) {
319 // Extract the original Ethernet frame from the packet information
320 InboundPacket pkt = context.inPacket();
321 Ethernet ethPkt = pkt.parsed();
322 if (ethPkt == null) {
323 return;
324 }
325
326 ARP arpPacket = (ARP) ethPkt.getPayload();
327
328 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
329 getTargetProtocolAddress());
330
331 String serialNo = ipToSnMap.get(targetAddress);
332 if (serialNo == null) {
333 log.info("No mapping found for ARP reply, target address {}",
334 targetAddress);
335 return;
336 }
337 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
338 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100339 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100340 return;
341 }
342
343 ARP arpReply = (ARP) arpPacket.clone();
344 arpReply.setOpCode(ARP.OP_REPLY);
345 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
346 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
347 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
348 arpReply.setSenderHardwareAddress(senderMac.toBytes());
349
350 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
351
352 // Ethernet Frame.
353 Ethernet ethReply = new Ethernet();
354 ethReply.setSourceMACAddress(senderMac);
355 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
356 ethReply.setEtherType(Ethernet.TYPE_ARP);
357 ethReply.setVlanID(radiusVlanID);
358 ethReply.setPriorityCode(ethPkt.getPriorityCode());
359
360 ethReply.setPayload(arpReply);
361 sendFromRadiusServerPort(ethReply);
362 }
363
364 /**
365 * Handles IP packets from RADIUS server.
366 *
367 * @param context Context for the packet
368 */
369 private void handleIPv4PacketFromServer(PacketContext context) {
370 // Extract the original Ethernet frame from the packet information
371 InboundPacket pkt = context.inPacket();
372 Ethernet ethPkt = pkt.parsed();
373 if (ethPkt == null) {
374 return;
375 }
376
377 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
378
379 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
380 UDP udpPacket = (UDP) ipv4Packet.getPayload();
381
382 if (udpPacket.getSourcePort() == radiusServerPort) {
383 //This packet is RADIUS packet from the server.
384 RADIUS radiusMsg;
385 try {
386 radiusMsg =
387 RADIUS.deserializer()
388 .deserialize(udpPacket.serialize(),
389 8,
390 udpPacket.getLength() - 8);
391 try {
kartikey dubeye1545422019-05-22 12:53:45 +0000392 aaaManager.aaaStatisticsManager.handleRoundtripTime(radiusMsg.getIdentifier());
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100393 aaaManager.handleRadiusPacket(radiusMsg);
394 } catch (StateMachineException sme) {
395 log.error("Illegal state machine operation", sme);
396 }
397 } catch (DeserializationException dex) {
398 log.error("Cannot deserialize packet", dex);
399 }
400 }
401 }
402 }
403
404 /**
405 * Handles Mastership changes for the devices which connect
406 * to the RADIUS server.
407 */
408 private class InnerMastershipListener implements MastershipListener {
409 @Override
410 public void event(MastershipEvent event) {
411 if (radiusServerConnectPoint != null &&
412 radiusServerConnectPoint.deviceId().
413 equals(event.subject())) {
414 log.trace("Mastership Event recevived for {}", event.subject());
415 // mastership of the device for our connect point has changed
416 // reselect
417 initializeLocalState();
418 }
419 }
420 }
421
422 /**
423 * Handles Device status change for the devices which connect
424 * to the RADIUS server.
425 */
426 private class InnerDeviceListener implements DeviceListener {
427 @Override
428 public void event(DeviceEvent event) {
429 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
430 if (radiusServerConnectPoint == null) {
431 switch (event.type()) {
432 case DEVICE_ADDED:
433 case DEVICE_AVAILABILITY_CHANGED:
434 // some device is available check if we can get one
435 initializeLocalState();
436 break;
437 default:
438 break;
439 }
440 return;
441 }
442 if (radiusServerConnectPoint.deviceId().
443 equals(event.subject().id())) {
444 switch (event.type()) {
445 case DEVICE_AVAILABILITY_CHANGED:
446 case DEVICE_REMOVED:
447 case DEVICE_SUSPENDED:
448 // state of our device has changed, check if we need
449 // to re-select
450 initializeLocalState();
451 break;
452 default:
453 break;
454 }
455 }
456 }
457 }
458}