blob: e1b8bb9bd42bd19ebde47211c06a3606761b0298 [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;
112
113 ConnectPoint radiusServerConnectPoint = null;
114
115 InnerMastershipListener changeListener = new InnerMastershipListener();
116 InnerDeviceListener deviceListener = new InnerDeviceListener();
117
118 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
119 MastershipService masService, DeviceService devService,
Gamze Abaka1cfdb192018-10-25 11:39:19 +0000120 BaseInformationService<SubscriberAndDeviceInformation> subsService,
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100121 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
122 this.appId = appId;
123 this.packetService = pktService;
124 this.mastershipService = masService;
125 this.deviceService = devService;
126 this.subsService = subsService;
127 this.pktCustomizer = pktCustomizer;
128 this.aaaManager = aaaManager;
129
130 ipToSnMap = Maps.newConcurrentMap();
131 mastershipService.addListener(changeListener);
132 deviceService.addListener(deviceListener);
133
Amit Ghoshcf39d972017-08-16 08:15:20 +0100134 log.info("Created PortBased");
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100135 }
136
137 private void initializeLocalState() {
138 synchronized (this) {
139 radiusServerConnectPoint = null;
140 if (radiusConnectPoints != null) {
141 // find a connect point through a device for which we are master
142 for (ConnectPoint cp: radiusConnectPoints) {
143 if (mastershipService.isLocalMaster(cp.deviceId())) {
144 if (deviceService.isAvailable(cp.deviceId())) {
145 radiusServerConnectPoint = cp;
146 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100147 break;
148 }
149 }
150 }
151
Amit Ghoshcf39d972017-08-16 08:15:20 +0100152 log.info("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100153
154 if (radiusServerConnectPoint == null) {
155 log.error("Master of none, can't send radius Message to server");
156 }
157 }
158 }
159
160 @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
260 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
261
262 if (deviceInfo == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100263 log.warn("No Device found with SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100264 return;
265 }
266 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
267
268 // send the message out
269 sendFromRadiusServerPort(pktCustomizer.
270 customizeEthernetIPHeaders(ethReply, inPkt));
271 }
272
273 /**
274 * Sends packet to the RADIUS server using one of the switch ports.
275 *
276 * @param packet Ethernet packet to be sent
277 */
278 private void sendFromRadiusServerPort(Ethernet packet) {
279 if (radiusServerConnectPoint != null) {
280 log.trace("AAA Manager sending Ethernet packet = {}", packet);
281 TrafficTreatment t = DefaultTrafficTreatment.builder()
282 .setOutput(radiusServerConnectPoint.port()).build();
283 OutboundPacket o = new DefaultOutboundPacket(
284 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
285 packetService.emit(o);
286 } else {
287 log.error("Unable to send RADIUS packet, connectPoint is null");
288 }
289 }
290
291 @Override
292 public void handlePacketFromServer(PacketContext context) {
293 // Extract the original Ethernet frame from the packet information
294 InboundPacket pkt = context.inPacket();
295 Ethernet ethPkt = pkt.parsed();
296 if (ethPkt == null) {
297 return;
298 }
299
300 // identify if incoming packet
301 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
302 case ARP:
303 handleArpPacketFromServer(context);
304 break;
305 case IPV4:
306 handleIPv4PacketFromServer(context);
307 break;
308 default:
309 log.debug("Skipping Ethernet packet type {}",
310 EthType.EtherType.lookup(ethPkt.getEtherType()));
311 }
312 }
313
314 /**
315 * Handles ARP packets from RADIUS server.
316 *
317 * @param context Context for the packet
318 */
319 private void handleArpPacketFromServer(PacketContext context) {
320 // Extract the original Ethernet frame from the packet information
321 InboundPacket pkt = context.inPacket();
322 Ethernet ethPkt = pkt.parsed();
323 if (ethPkt == null) {
324 return;
325 }
326
327 ARP arpPacket = (ARP) ethPkt.getPayload();
328
329 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
330 getTargetProtocolAddress());
331
332 String serialNo = ipToSnMap.get(targetAddress);
333 if (serialNo == null) {
334 log.info("No mapping found for ARP reply, target address {}",
335 targetAddress);
336 return;
337 }
338 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
339 if (senderMac == null) {
Amit Ghoshcf39d972017-08-16 08:15:20 +0100340 log.warn("ARP resolution, MAC address not found for SN {}", serialNo);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100341 return;
342 }
343
344 ARP arpReply = (ARP) arpPacket.clone();
345 arpReply.setOpCode(ARP.OP_REPLY);
346 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
347 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
348 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
349 arpReply.setSenderHardwareAddress(senderMac.toBytes());
350
351 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
352
353 // Ethernet Frame.
354 Ethernet ethReply = new Ethernet();
355 ethReply.setSourceMACAddress(senderMac);
356 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
357 ethReply.setEtherType(Ethernet.TYPE_ARP);
358 ethReply.setVlanID(radiusVlanID);
359 ethReply.setPriorityCode(ethPkt.getPriorityCode());
360
361 ethReply.setPayload(arpReply);
362 sendFromRadiusServerPort(ethReply);
363 }
364
365 /**
366 * Handles IP packets from RADIUS server.
367 *
368 * @param context Context for the packet
369 */
370 private void handleIPv4PacketFromServer(PacketContext context) {
371 // Extract the original Ethernet frame from the packet information
372 InboundPacket pkt = context.inPacket();
373 Ethernet ethPkt = pkt.parsed();
374 if (ethPkt == null) {
375 return;
376 }
377
378 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
379
380 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
381 UDP udpPacket = (UDP) ipv4Packet.getPayload();
382
383 if (udpPacket.getSourcePort() == radiusServerPort) {
384 //This packet is RADIUS packet from the server.
385 RADIUS radiusMsg;
386 try {
387 radiusMsg =
388 RADIUS.deserializer()
389 .deserialize(udpPacket.serialize(),
390 8,
391 udpPacket.getLength() - 8);
392 try {
393 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}