blob: e402aae78821cfcc3da1f3cf6972e1be06cbfac1 [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
Saurav Das987441a2018-09-18 16:33:47 -070018import static org.onosproject.net.packet.PacketPriority.CONTROL;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import java.io.IOException;
22import java.net.DatagramPacket;
23import java.net.DatagramSocket;
24import java.net.InetAddress;
25import java.net.InetSocketAddress;
26import java.net.UnknownHostException;
27import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010030import org.onlab.packet.DeserializationException;
31import org.onlab.packet.EthType;
32import org.onlab.packet.Ethernet;
33import org.onlab.packet.RADIUS;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010034import org.onosproject.core.ApplicationId;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.packet.InboundPacket;
38import org.onosproject.net.packet.PacketContext;
39import org.onosproject.net.packet.PacketService;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010040import org.slf4j.Logger;
41
Saurav Das987441a2018-09-18 16:33:47 -070042import com.google.common.util.concurrent.ThreadFactoryBuilder;
Jonathan Hartf935b122018-07-11 16:16:02 -070043
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010044/**
45 * Handles Socket based communication with the RADIUS server.
46 */
47public class SocketBasedRadiusCommunicator implements RadiusCommunicator {
48
49 // for verbose output
50 private final Logger log = getLogger(getClass());
51
52 // our unique identifier
53 private ApplicationId appId;
54
55 // to receive Packet-in events that we'll respond to
56 PacketService packetService;
57
58 // Socket used for UDP communications with RADIUS server
59 private DatagramSocket radiusSocket;
60
Jonathan Hartf935b122018-07-11 16:16:02 -070061 private String radiusHost;
62
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010063 // Parsed RADIUS server addresses
64 protected InetAddress radiusIpAddress;
65
66 // RADIUS server TCP port number
67 protected short radiusServerPort;
68
69 // Executor for RADIUS communication thread
70 private ExecutorService executor;
71
72 AaaManager aaaManager;
73
74 SocketBasedRadiusCommunicator(ApplicationId appId, PacketService pktService,
75 AaaManager aaaManager) {
76 this.appId = appId;
77 this.packetService = pktService;
78 this.aaaManager = aaaManager;
79 }
80
81 @Override
82 public void initializeLocalState(AaaConfig newCfg) {
83 if (newCfg.radiusIp() != null) {
84 radiusIpAddress = newCfg.radiusIp();
85 }
86 radiusServerPort = newCfg.radiusServerUdpPort();
Jonathan Hartf935b122018-07-11 16:16:02 -070087 radiusHost = newCfg.radiusHostName();
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010088
89 try {
90 radiusSocket = new DatagramSocket(null);
91 radiusSocket.setReuseAddress(true);
92 radiusSocket.bind(new InetSocketAddress(radiusServerPort));
93 } catch (Exception ex) {
94 log.error("Can't open RADIUS socket", ex);
95 }
96
Matt Jeanneret2ff1a782018-06-13 15:24:25 -040097 log.info("Remote RADIUS Server: {}:{}", radiusIpAddress, radiusServerPort);
98
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010099 executor = Executors.newSingleThreadExecutor(
100 new ThreadFactoryBuilder()
101 .setNameFormat("AAA-radius-%d").build());
102 executor.execute(radiusListener);
103 }
104
105 @Override
106 public void clearLocalState() {
107 radiusSocket.close();
108 executor.shutdownNow();
109 }
110
111 @Override
Deepa Vaddireddyd87f2872017-10-24 07:58:11 +0000112 public void deactivate() {
113 clearLocalState();
114 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100115
116 @Override
117 public void requestIntercepts() {
118 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
119 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
120 packetService.requestPackets(selector.build(), CONTROL, appId);
121 }
122
123 @Override
124 public void withdrawIntercepts() {
125 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
126 selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
127 packetService.cancelPackets(selector.build(), CONTROL, appId);
128 }
129
130 @Override
131 public void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
132 try {
133 final byte[] data = radiusPacket.serialize();
134 final DatagramSocket socket = radiusSocket;
135
Jonathan Hartf935b122018-07-11 16:16:02 -0700136 try {
137 InetAddress address;
138 if (radiusHost != null) {
139 address = InetAddress.getByName(radiusHost);
140 } else {
141 address = radiusIpAddress;
142 }
143 DatagramPacket packet =
144 new DatagramPacket(data, data.length, address, radiusServerPort);
Saurav Das987441a2018-09-18 16:33:47 -0700145 if (log.isTraceEnabled()) {
146 log.trace("Sending packet {} to Radius Server {}:{} using socket",
147 radiusPacket, address, radiusServerPort);
148 }
Jonathan Hartf935b122018-07-11 16:16:02 -0700149 socket.send(packet);
Jonathan Hartf935b122018-07-11 16:16:02 -0700150 } catch (UnknownHostException uhe) {
151 log.warn("Unable to resolve host {}", radiusHost);
152 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100153 } catch (IOException e) {
154 log.info("Cannot send packet to RADIUS server", e);
155 }
156 }
157
158 @Override
159 public void handlePacketFromServer(PacketContext context) {
160 InboundPacket pkt = context.inPacket();
161 Ethernet ethPkt = pkt.parsed();
Saurav Das987441a2018-09-18 16:33:47 -0700162 if (log.isTraceEnabled() && ethPkt.getEtherType() != Ethernet.TYPE_LLDP
163 && ethPkt.getEtherType() != Ethernet.TYPE_BSN) {
164 log.trace("Skipping Ethernet packet type {}",
165 EthType.EtherType.lookup(ethPkt.getEtherType()));
166 }
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100167 }
168
169 class RadiusListener implements Runnable {
170
171 @Override
172 public void run() {
173 boolean done = false;
174 int packetNumber = 1;
175
176 log.info("UDP listener thread starting up");
177 RADIUS inboundRadiusPacket;
178 while (!done) {
179 try {
180 byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH];
181 DatagramPacket inboundBasePacket =
182 new DatagramPacket(packetBuffer, packetBuffer.length);
183 DatagramSocket socket = radiusSocket;
184 socket.receive(inboundBasePacket);
Matt Jeanneret2ff1a782018-06-13 15:24:25 -0400185 log.debug("Packet #{} received", packetNumber++);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100186 try {
187 inboundRadiusPacket =
188 RADIUS.deserializer()
189 .deserialize(inboundBasePacket.getData(),
190 0,
191 inboundBasePacket.getLength());
192 aaaManager.handleRadiusPacket(inboundRadiusPacket);
193 } catch (DeserializationException dex) {
194 log.error("Cannot deserialize packet", dex);
195 } catch (StateMachineException sme) {
196 log.error("Illegal state machine operation", sme);
197 }
198
199 } catch (IOException e) {
200 log.info("Socket was closed, exiting listener thread");
201 done = true;
202 }
203 }
204 }
205 }
206
207 RadiusListener radiusListener = new RadiusListener();
208}