2 * Copyright (c) 2004 University of Murcia. All rights reserved.
3 * --------------------------------------------------------------
4 * For more information, please see <http://www.umu.euro6ix.org/>.
7 package org.umu.cops.prpdp;
9 import org.pcmm.objects.MMVersionInfo;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import org.umu.cops.stack.*;
13 import org.umu.cops.stack.COPSError.ErrorTypes;
14 import org.umu.cops.stack.COPSHeader.OPCode;
16 import java.io.IOException;
17 import java.net.InetAddress;
18 import java.net.InetSocketAddress;
19 import java.net.Socket;
22 * Core PDP agent for managing the connection to one PDP.
24 public class COPSPdpAgent {
26 private static final Logger logger = LoggerFactory.getLogger(COPSPdpAgent.class);
28 /** Well-known port for COPS */
29 public static final int WELL_KNOWN_PDP_PORT = 3288;
30 /** Default keep-alive timer value (secs) */
31 public static final short KA_TIMER_VALUE = 30;
32 /** Default accounting timer value (secs) */
33 public static final short ACCT_TIMER_VALUE = 0;
38 protected final String _host;
43 protected final int _serverPort;
46 * Client-type of connecting PEP
48 protected final short _clientType;
51 * Accounting timer (secs)
53 protected final short _acctTimer;
56 * Keep-alive timer (secs)
58 protected final short _kaTimer;
61 * Policy data processing object
63 protected final COPSPdpDataProcess _process;
65 // Next two attributes are initialized when connected
67 * The Socket connection to the PEP
69 protected transient Socket _socket;
74 protected transient COPSHandle _handle;
76 // Next three attributes are initialized after the client accepts
78 * Holds the last PEP ID processed
80 protected transient COPSPepId _pepId;
83 * the PDP connection connection
85 protected transient COPSPdpConnection _pdpConn;
88 * The handle to the tread accepting messages from the PDP
90 protected transient Thread _thread;
95 * @param host PDP agent host name
96 * @param port Port to listen to
97 * @param clientType COPS Client-type
98 * @param process Object to perform policy data processing
100 public COPSPdpAgent(final String host, final int port, final short clientType, final COPSPdpDataProcess process) {
102 this._serverPort = port;
104 this._kaTimer = KA_TIMER_VALUE;
105 this._acctTimer = ACCT_TIMER_VALUE;
107 this._clientType = clientType;
108 this._process = process;
112 * Returns handle after connect() has successfully been executed
113 * @return - the handle
115 public COPSHandle getClientHandle() {
120 * Returns handle after connect() has successfully been executed
121 * @return - the handle
123 public Socket getSocket() {
129 * @throws java.net.UnknownHostException
130 * @throws java.io.IOException
131 * @throws COPSException
133 public void connect() throws IOException, COPSException {
134 // Create Socket and send OPN
135 _socket = new Socket();
136 _socket.connect(new InetSocketAddress(InetAddress.getByName(_host), _serverPort));
137 logger.info("PDP Socket Opened. Waiting to receive client-open message");
138 final COPSMsg msg = COPSTransceiver.receiveMsg(_socket);
139 logger.debug("Message received of type - " + msg.getHeader().getOpCode());
140 if (msg.getHeader().getOpCode().equals(OPCode.OPN)) {
141 handleClientOpenMsg(_socket, msg);
145 } catch (Exception ex) {
146 logger.error("Unexpected error closing socket", ex);
152 * Disconnects a PEP and stops the listener thread
153 * @param error COPS Error to be reported as a reason
155 public void disconnect(final COPSError error) {
156 if (_pdpConn != null) {
157 sendCloseMessage(_socket, error.getErrCode(), error.getErrSubCode(), "Disconnecting from PDP requested");
160 logger.warn("Unable to locate PDP connection. Cannot close");
162 if (_thread != null) _thread.interrupt();
163 else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
165 if (_socket != null && _socket.isConnected())
168 } catch (IOException e) {
169 logger.error("Error closing socket", e);
179 * Requests a COPS sync for a PEP
180 * @throws COPSException
181 * @throws COPSPdpException
183 public void sync() throws COPSException {
184 if (_pdpConn != null) _pdpConn.syncAllRequestState();
185 else logger.warn("Unable to sync, not connected to a PEP");
189 * Handles a COPS client-open message and sets the _pepId, _handle, _pdpConn, & _thread objects in the process
190 * as well as starts the PDP connection thread for receiving other COPS messages from the PDP
191 * @param conn Socket to the PEP
192 * @param msg <tt>COPSMsg</tt> holding the client-open message
193 * @throws COPSException
194 * @throws IOException
196 protected void handleClientOpenMsg(final Socket conn, final COPSMsg msg) throws COPSException, IOException {
197 logger.info("Processing client open message");
199 if (_pepId != null) {
200 throw new COPSException("Connection already opened");
203 final COPSClientOpenMsg cMsg = (COPSClientOpenMsg) msg;
204 _pepId = cMsg.getPepId();
206 // Validate Client Type
207 if (msg.getHeader().getClientType() != _clientType) {
208 sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE, ErrorTypes.NA,
209 "Unsupported client type");
212 // PEPId is mandatory
213 if (_pepId == null) {
214 sendCloseMessage(conn, ErrorTypes.MANDATORY_OBJECT_MISSING, ErrorTypes.NA,
215 "Mandatory COPS object missing (PEPId)");
218 // TODO - Determine if I should be checking for the PDPAddress and Integrity objects on the message too???
221 if ( (cMsg.getClientSI() != null) || (cMsg.getPdpAddress() != null) || (cMsg.getIntegrity() != null)) {
222 sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE,
223 "Unsupported objects (ClientSI, PdpAddress, Integrity)");
227 if ((cMsg.getClientSI() == null) ) {
228 sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA,
229 "Unsupported objects (PdpAddress, Integrity)");
231 final MMVersionInfo _mminfo = new MMVersionInfo(cMsg.getClientSI().getData().getData());
232 logger.debug("CMTS sent MMVersion info : major:" + _mminfo.getMajorVersionNB() + " minor:" +
233 _mminfo.getMinorVersionNB());
236 acceptConnection(conn);
238 _handle = handleAcceptResponse(conn);
239 if (_handle != null) {
240 // Connection accepted
241 _pdpConn = setputPdpConnection(conn, _handle);
242 _thread = new Thread(_pdpConn, "PDP Agent for PEP ID " + _pepId.getData().str());
245 throw new COPSException("Unable to connect to PDP");
250 * Creates and sends a client close message
251 * @param conn - the socket connection
252 * @param errorType - the error type to send
253 * @param msg - the error message to log
255 private void sendCloseMessage(final Socket conn, final ErrorTypes errorType, final ErrorTypes errorSubType,
257 final COPSClientCloseMsg closeMsg = new COPSClientCloseMsg(_clientType,
258 new COPSError(errorType, errorSubType), null, null);
260 logger.info("Sending client-close message. Reason: " + msg);
261 closeMsg.writeData(conn);
262 } catch (IOException unae) {
263 logger.error("Exception writing data", unae);
268 * Sends a client-accept message back to the PDP
269 * @param conn - the socket connection to the PDP
270 * @throws IOException
272 private void acceptConnection(final Socket conn) throws IOException {
273 final COPSClientAcceptMsg acceptMsg;
275 acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer),
276 new COPSAcctTimer(_acctTimer), null);
278 acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer) ,null, null);
279 acceptMsg.writeData(conn);
283 * Waits for the response back from the PDP and handles it appropriately. When successful, the handle to the
284 * client is returned.
285 * @param conn - the socket connection to the PDP
286 * @return - the handle or null if not successful
288 private COPSHandle handleAcceptResponse(final Socket conn) {
290 logger.debug("handleClientOpenMsg() - Waiting to receive message");
291 final COPSMsg rmsg = COPSTransceiver.receiveMsg(conn);
292 logger.debug("Received message of type - " + rmsg.getHeader().getOpCode());
294 if (rmsg.getHeader().getOpCode().equals(OPCode.CC)) {
295 logger.info("Received client-close message");
296 sendCloseMessage(conn, ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA, "Received client-close message");
300 if (rmsg.getHeader().getOpCode().equals(OPCode.REQ)) {
301 final COPSReqMsg rMsg = (COPSReqMsg) rmsg;
302 return rMsg.getClientHandle();
304 sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA, "Received unknown object");
308 } catch (Exception e) {
309 logger.error("Error COPSTransceiver.receiveMsg", e);
315 * Creates the PDP connection object
316 * @param conn - the socket connection to the PDP
317 * @param handle - the client's handle
318 * @return - the PDP connection object
320 protected COPSPdpConnection setputPdpConnection(final Socket conn, final COPSHandle handle) {
321 logger.debug("PDPCOPSConnection");
322 final COPSPdpConnection pdpConn = new COPSPdpConnection(_pepId, conn, _process, _kaTimer, _acctTimer);
324 // XXX - handleRequestMsg
325 // XXX - check handle is valid
326 final COPSPdpReqStateMan man = new COPSPdpReqStateMan(_clientType, handle, _process, conn);
327 pdpConn.addStateMan(handle, man);
328 // XXX - End handleRequestMsg
330 logger.info("Starting PDP connection thread to - " + _host);