blob: 6e256b7a404c928dea7bd733abf249c348d601ae [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 */
16package org.opencord.aaa;
17
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
47import org.opencord.sadis.SubscriberAndDeviceInformation;
48import org.opencord.sadis.SubscriberAndDeviceInformationService;
49
50import org.slf4j.Logger;
51
52import com.google.common.collect.Maps;
53
54import static org.onosproject.net.packet.PacketPriority.CONTROL;
55import static org.slf4j.LoggerFactory.getLogger;
56
57import java.net.InetAddress;
58import java.nio.ByteBuffer;
59import java.util.Map;
60import java.util.Set;
61
62/**
63 * Handles communication with the RADIUS server through ports
64 * of the SDN switches.
65 */
66public class PortBasedRadiusCommunicator implements RadiusCommunicator {
67
68 // for verbose output
69 private final Logger log = getLogger(getClass());
70
71 // our unique identifier
72 private ApplicationId appId;
73
74 // to receive Packet-in events that we'll respond to
75 PacketService packetService;
76
77 DeviceService deviceService;
78
79 MastershipService mastershipService;
80
81 SubscriberAndDeviceInformationService subsService;
82
83 // to store local mapping of IP Address and Serial No of Device
84 private Map<Ip4Address, String> ipToSnMap;
85
86 // connect points to the RADIUS server
87 Set<ConnectPoint> radiusConnectPoints;
88
89 // Parsed RADIUS server addresses
90 protected InetAddress radiusIpAddress;
91
92 // RADIUS server TCP port number
93 protected short radiusServerPort;
94
95 protected String radiusMacAddress;
96
97 // NAS IP address
98 protected InetAddress nasIpAddress;
99
100 protected String nasMacAddress;
101
102 // RADIUS server Vlan ID
103 private short radiusVlanID;
104
105 // RADIUS p-bit
106 private byte radiusPBit;
107
108 PacketCustomizer pktCustomizer;
109 AaaManager aaaManager;
110
111 ConnectPoint radiusServerConnectPoint = null;
112
113 InnerMastershipListener changeListener = new InnerMastershipListener();
114 InnerDeviceListener deviceListener = new InnerDeviceListener();
115
116 PortBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
117 MastershipService masService, DeviceService devService,
118 SubscriberAndDeviceInformationService subsService,
119 PacketCustomizer pktCustomizer, AaaManager aaaManager) {
120 this.appId = appId;
121 this.packetService = pktService;
122 this.mastershipService = masService;
123 this.deviceService = devService;
124 this.subsService = subsService;
125 this.pktCustomizer = pktCustomizer;
126 this.aaaManager = aaaManager;
127
128 ipToSnMap = Maps.newConcurrentMap();
129 mastershipService.addListener(changeListener);
130 deviceService.addListener(deviceListener);
131
132 log.error("Created PortBased");
133 }
134
135 private void initializeLocalState() {
136 synchronized (this) {
137 radiusServerConnectPoint = null;
138 if (radiusConnectPoints != null) {
139 // find a connect point through a device for which we are master
140 for (ConnectPoint cp: radiusConnectPoints) {
141 if (mastershipService.isLocalMaster(cp.deviceId())) {
142 if (deviceService.isAvailable(cp.deviceId())) {
143 radiusServerConnectPoint = cp;
144 }
145 log.warn("RADIUS connectPoint selected is {}", cp);
146 break;
147 }
148 }
149 }
150
151 log.warn("RADIUS connectPoint in initializeLocalState is {}", radiusServerConnectPoint);
152
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
184 public void clearLocalState() {}
185
186 @Override
187 public void deactivate() {
188 mastershipService.removeListener(changeListener);
189 deviceService.removeListener(deviceListener);
190 }
191
192 @Override
193 public void requestIntercepts() {
194 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
195 .matchEthType(Ethernet.TYPE_ARP);
196 packetService.requestPackets(selectorArpServer.build(), CONTROL, appId);
197
198 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
199 .matchEthType(Ethernet.TYPE_IPV4)
200 .matchIPProtocol(IPv4.PROTOCOL_UDP)
201 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
202 packetService.requestPackets(selectorServer.build(), CONTROL, appId);
203 }
204
205 @Override
206 public void withdrawIntercepts() {
207 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
208 .matchEthType(Ethernet.TYPE_ARP);
209 packetService.cancelPackets(selectorArpServer.build(), CONTROL, appId);
210
211 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
212 .matchEthType(Ethernet.TYPE_IPV4)
213 .matchIPProtocol(IPv4.PROTOCOL_UDP)
214 .matchUdpSrc(TpPort.tpPort(radiusServerPort));
215 packetService.cancelPackets(selectorServer.build(), CONTROL, appId);
216 }
217
218 @Override
219 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
220 // create the packet
221 Ethernet ethReply = new Ethernet();
222 ethReply.setSourceMACAddress(nasMacAddress);
223 ethReply.setDestinationMACAddress(radiusMacAddress);
224 ethReply.setEtherType(Ethernet.TYPE_IPV4);
225 ethReply.setVlanID(radiusVlanID);
226 ethReply.setPriorityCode(radiusPBit);
227
228 IPv4 ipv4Packet = new IPv4();
229 ipv4Packet.setTtl((byte) 64);
230 ipv4Packet.setSourceAddress(Ip4Address.
231 valueOf(nasIpAddress).toInt());
232 ipv4Packet.setDestinationAddress(Ip4Address.
233 valueOf(radiusIpAddress).toInt());
234
235 UDP udpPacket = new UDP();
236 udpPacket.setSourcePort(radiusServerPort);
237 udpPacket.setDestinationPort(radiusServerPort);
238
239 udpPacket.setPayload(radiusPacket);
240 ipv4Packet.setPayload(udpPacket);
241 ethReply.setPayload(ipv4Packet);
242
243 // store the IP address and SN of the device, later to be used
244 // for ARP responses
245 String serialNo = deviceService.getDevice(inPkt.
246 receivedFrom().deviceId()).serialNumber();
247
248 SubscriberAndDeviceInformation deviceInfo = subsService.get(serialNo);
249
250 if (deviceInfo == null) {
251 log.error("No Device found with SN {}", serialNo);
252 return;
253 }
254 ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
255
256 // send the message out
257 sendFromRadiusServerPort(pktCustomizer.
258 customizeEthernetIPHeaders(ethReply, inPkt));
259 }
260
261 /**
262 * Sends packet to the RADIUS server using one of the switch ports.
263 *
264 * @param packet Ethernet packet to be sent
265 */
266 private void sendFromRadiusServerPort(Ethernet packet) {
267 if (radiusServerConnectPoint != null) {
268 log.trace("AAA Manager sending Ethernet packet = {}", packet);
269 TrafficTreatment t = DefaultTrafficTreatment.builder()
270 .setOutput(radiusServerConnectPoint.port()).build();
271 OutboundPacket o = new DefaultOutboundPacket(
272 radiusServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
273 packetService.emit(o);
274 } else {
275 log.error("Unable to send RADIUS packet, connectPoint is null");
276 }
277 }
278
279 @Override
280 public void handlePacketFromServer(PacketContext context) {
281 // Extract the original Ethernet frame from the packet information
282 InboundPacket pkt = context.inPacket();
283 Ethernet ethPkt = pkt.parsed();
284 if (ethPkt == null) {
285 return;
286 }
287
288 // identify if incoming packet
289 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
290 case ARP:
291 handleArpPacketFromServer(context);
292 break;
293 case IPV4:
294 handleIPv4PacketFromServer(context);
295 break;
296 default:
297 log.debug("Skipping Ethernet packet type {}",
298 EthType.EtherType.lookup(ethPkt.getEtherType()));
299 }
300 }
301
302 /**
303 * Handles ARP packets from RADIUS server.
304 *
305 * @param context Context for the packet
306 */
307 private void handleArpPacketFromServer(PacketContext context) {
308 // Extract the original Ethernet frame from the packet information
309 InboundPacket pkt = context.inPacket();
310 Ethernet ethPkt = pkt.parsed();
311 if (ethPkt == null) {
312 return;
313 }
314
315 ARP arpPacket = (ARP) ethPkt.getPayload();
316
317 Ip4Address targetAddress = Ip4Address.valueOf(arpPacket.
318 getTargetProtocolAddress());
319
320 String serialNo = ipToSnMap.get(targetAddress);
321 if (serialNo == null) {
322 log.info("No mapping found for ARP reply, target address {}",
323 targetAddress);
324 return;
325 }
326 MacAddress senderMac = subsService.get(serialNo).hardwareIdentifier();
327 if (senderMac == null) {
328 log.error("ARP resolution, MAC address not found for SN {}", serialNo);
329 return;
330 }
331
332 ARP arpReply = (ARP) arpPacket.clone();
333 arpReply.setOpCode(ARP.OP_REPLY);
334 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
335 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
336 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
337 arpReply.setSenderHardwareAddress(senderMac.toBytes());
338
339 log.debug("AAA Manager: Query for ARP of IP : {}", arpPacket.getTargetProtocolAddress());
340
341 // Ethernet Frame.
342 Ethernet ethReply = new Ethernet();
343 ethReply.setSourceMACAddress(senderMac);
344 ethReply.setDestinationMACAddress(ethPkt.getSourceMAC());
345 ethReply.setEtherType(Ethernet.TYPE_ARP);
346 ethReply.setVlanID(radiusVlanID);
347 ethReply.setPriorityCode(ethPkt.getPriorityCode());
348
349 ethReply.setPayload(arpReply);
350 sendFromRadiusServerPort(ethReply);
351 }
352
353 /**
354 * Handles IP packets from RADIUS server.
355 *
356 * @param context Context for the packet
357 */
358 private void handleIPv4PacketFromServer(PacketContext context) {
359 // Extract the original Ethernet frame from the packet information
360 InboundPacket pkt = context.inPacket();
361 Ethernet ethPkt = pkt.parsed();
362 if (ethPkt == null) {
363 return;
364 }
365
366 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
367
368 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
369 UDP udpPacket = (UDP) ipv4Packet.getPayload();
370
371 if (udpPacket.getSourcePort() == radiusServerPort) {
372 //This packet is RADIUS packet from the server.
373 RADIUS radiusMsg;
374 try {
375 radiusMsg =
376 RADIUS.deserializer()
377 .deserialize(udpPacket.serialize(),
378 8,
379 udpPacket.getLength() - 8);
380 try {
381 aaaManager.handleRadiusPacket(radiusMsg);
382 } catch (StateMachineException sme) {
383 log.error("Illegal state machine operation", sme);
384 }
385 } catch (DeserializationException dex) {
386 log.error("Cannot deserialize packet", dex);
387 }
388 }
389 }
390 }
391
392 /**
393 * Handles Mastership changes for the devices which connect
394 * to the RADIUS server.
395 */
396 private class InnerMastershipListener implements MastershipListener {
397 @Override
398 public void event(MastershipEvent event) {
399 if (radiusServerConnectPoint != null &&
400 radiusServerConnectPoint.deviceId().
401 equals(event.subject())) {
402 log.trace("Mastership Event recevived for {}", event.subject());
403 // mastership of the device for our connect point has changed
404 // reselect
405 initializeLocalState();
406 }
407 }
408 }
409
410 /**
411 * Handles Device status change for the devices which connect
412 * to the RADIUS server.
413 */
414 private class InnerDeviceListener implements DeviceListener {
415 @Override
416 public void event(DeviceEvent event) {
417 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
418 if (radiusServerConnectPoint == null) {
419 switch (event.type()) {
420 case DEVICE_ADDED:
421 case DEVICE_AVAILABILITY_CHANGED:
422 // some device is available check if we can get one
423 initializeLocalState();
424 break;
425 default:
426 break;
427 }
428 return;
429 }
430 if (radiusServerConnectPoint.deviceId().
431 equals(event.subject().id())) {
432 switch (event.type()) {
433 case DEVICE_AVAILABILITY_CHANGED:
434 case DEVICE_REMOVED:
435 case DEVICE_SUSPENDED:
436 // state of our device has changed, check if we need
437 // to re-select
438 initializeLocalState();
439 break;
440 default:
441 break;
442 }
443 }
444 }
445 }
446}