/*
* Copyright (c) 2004 University of Murcia. All rights reserved.
* --------------------------------------------------------------
* For more information, please see .
*/
package org.umu.cops.prpdp;
import org.pcmm.objects.MMVersionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.umu.cops.stack.*;
import org.umu.cops.stack.COPSError.ErrorTypes;
import org.umu.cops.stack.COPSHeader.OPCode;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
/**
* Core PDP agent for managing the connection to one PDP.
*/
public class COPSPdpAgent {
private static final Logger logger = LoggerFactory.getLogger(COPSPdpAgent.class);
/** Well-known port for COPS */
public static final int WELL_KNOWN_PDP_PORT = 3288;
/** Default keep-alive timer value (secs) */
public static final short KA_TIMER_VALUE = 30;
/** Default accounting timer value (secs) */
public static final short ACCT_TIMER_VALUE = 0;
/**
* PDP host address
*/
protected final String _host;
/**
* PDP host port
*/
protected final int _serverPort;
/**
* Client-type of connecting PEP
*/
protected final short _clientType;
/**
* Accounting timer (secs)
*/
protected final short _acctTimer;
/**
* Keep-alive timer (secs)
*/
protected final short _kaTimer;
/**
* Policy data processing object
*/
protected final COPSPdpDataProcess _process;
// Next two attributes are initialized when connected
/**
* The Socket connection to the PEP
*/
protected transient Socket _socket;
/**
* The PEP handle
*/
protected transient COPSHandle _handle;
// Next three attributes are initialized after the client accepts
/**
* Holds the last PEP ID processed
*/
protected transient COPSPepId _pepId;
/**
* the PDP connection connection
*/
protected transient COPSPdpConnection _pdpConn;
/**
* The handle to the tread accepting messages from the PDP
*/
protected transient Thread _thread;
/**
* Creates a PDP Agent
*
* @param host PDP agent host name
* @param port Port to listen to
* @param clientType COPS Client-type
* @param process Object to perform policy data processing
*/
public COPSPdpAgent(final String host, final int port, final short clientType, final COPSPdpDataProcess process) {
this._host = host;
this._serverPort = port;
this._kaTimer = KA_TIMER_VALUE;
this._acctTimer = ACCT_TIMER_VALUE;
this._clientType = clientType;
this._process = process;
}
/**
* Returns handle after connect() has successfully been executed
* @return - the handle
*/
public COPSHandle getClientHandle() {
return _handle;
}
/**
* Returns handle after connect() has successfully been executed
* @return - the handle
*/
public Socket getSocket() {
return _socket;
}
/**
* Connects to a PDP
* @throws java.net.UnknownHostException
* @throws java.io.IOException
* @throws COPSException
*/
public void connect() throws IOException, COPSException {
// Create Socket and send OPN
final InetAddress addr = InetAddress.getByName(_host);
_socket = new Socket(addr, _serverPort);
logger.info("PDP Socket Opened. Waiting to receive client-open message");
final COPSMsg msg = COPSTransceiver.receiveMsg(_socket);
logger.debug("Message received of type - " + msg.getHeader().getOpCode());
if (msg.getHeader().getOpCode().equals(OPCode.OPN)) {
handleClientOpenMsg(_socket, msg);
} else {
try {
_socket.close();
} catch (Exception ex) {
logger.error("Unexpected error closing socket", ex);
}
}
}
/**
* Disconnects a PEP and stops the listener thread
* @param error COPS Error to be reported as a reason
*/
public void disconnect(final COPSError error) {
if (_pdpConn != null) {
sendCloseMessage(_socket, error.getErrCode(), error.getErrSubCode(), "Disconnecting from PDP requested");
_pdpConn.close();
} else {
logger.warn("Unable to locate PDP connection. Cannot close");
}
if (_thread != null) _thread.interrupt();
else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
if (_socket.isConnected())
try {
_socket.close();
} catch (IOException e) {
logger.error("Error closing socket", e);
}
_socket = null;
_pepId = null;
_pdpConn = null;
_thread = null;
}
/**
* Requests a COPS sync for a PEP
* @throws COPSException
* @throws COPSPdpException
*/
public void sync() throws COPSException {
if (_pdpConn != null) _pdpConn.syncAllRequestState();
else logger.warn("Unable to sync, not connected to a PEP");
}
/**
* Handles a COPS client-open message and sets the _pepId, _handle, _pdpConn, & _thread objects in the process
* as well as starts the PDP connection thread for receiving other COPS messages from the PDP
* @param conn Socket to the PEP
* @param msg COPSMsg holding the client-open message
* @throws COPSException
* @throws IOException
*/
protected void handleClientOpenMsg(final Socket conn, final COPSMsg msg) throws COPSException, IOException {
logger.info("Processing client open message");
if (_pepId != null) {
throw new COPSException("Connection already opened");
}
final COPSClientOpenMsg cMsg = (COPSClientOpenMsg) msg;
_pepId = cMsg.getPepId();
// Validate Client Type
if (msg.getHeader().getClientType() != _clientType) {
sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE, ErrorTypes.NA,
"Unsupported client type");
}
// PEPId is mandatory
if (_pepId == null) {
sendCloseMessage(conn, ErrorTypes.MANDATORY_OBJECT_MISSING, ErrorTypes.NA,
"Mandatory COPS object missing (PEPId)");
}
// TODO - Determine if I should be checking for the PDPAddress and Integrity objects on the message too???
// Support
/*
if ( (cMsg.getClientSI() != null) || (cMsg.getPdpAddress() != null) || (cMsg.getIntegrity() != null)) {
sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE,
"Unsupported objects (ClientSI, PdpAddress, Integrity)");
}
*/
// Support
if ((cMsg.getClientSI() == null) ) {
sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA,
"Unsupported objects (PdpAddress, Integrity)");
} else {
final MMVersionInfo _mminfo = new MMVersionInfo(cMsg.getClientSI().getData().getData());
logger.debug("CMTS sent MMVersion info : major:" + _mminfo.getMajorVersionNB() + " minor:" +
_mminfo.getMinorVersionNB());
}
acceptConnection(conn);
_handle = handleAcceptResponse(conn);
if (_handle != null) {
// Connection accepted
_pdpConn = setputPdpConnection(conn, _handle);
_thread = new Thread(_pdpConn, "PDP Agent for PEP ID " + _pepId.getData().str());
_thread.start();
} else {
throw new COPSException("Unable to connect to PDP");
}
}
/**
* Creates and sends a client close message
* @param conn - the socket connection
* @param errorType - the error type to send
* @param msg - the error message to log
*/
private void sendCloseMessage(final Socket conn, final ErrorTypes errorType, final ErrorTypes errorSubType,
final String msg) {
final COPSClientCloseMsg closeMsg = new COPSClientCloseMsg(_clientType,
new COPSError(errorType, errorSubType), null, null);
try {
logger.info("Sending client-close message. Reason: " + msg);
closeMsg.writeData(conn);
} catch (IOException unae) {
logger.error("Exception writing data", unae);
}
}
/**
* Sends a client-accept message back to the PDP
* @param conn - the socket connection to the PDP
* @throws IOException
*/
private void acceptConnection(final Socket conn) throws IOException {
final COPSClientAcceptMsg acceptMsg;
if (_acctTimer != 0)
acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer),
new COPSAcctTimer(_acctTimer), null);
else
acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer) ,null, null);
acceptMsg.writeData(conn);
}
/**
* Waits for the response back from the PDP and handles it appropriately. When successful, the handle to the
* client is returned.
* @param conn - the socket connection to the PDP
* @return - the handle or null if not successful
*/
private COPSHandle handleAcceptResponse(final Socket conn) {
try {
logger.debug("handleClientOpenMsg() - Waiting to receive message");
final COPSMsg rmsg = COPSTransceiver.receiveMsg(conn);
logger.debug("Received message of type - " + rmsg.getHeader().getOpCode());
// Client-Close
if (rmsg.getHeader().getOpCode().equals(OPCode.CC)) {
logger.info("Received client-close message");
sendCloseMessage(conn, ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA, "Received client-close message");
return null;
} else {
// Request
if (rmsg.getHeader().getOpCode().equals(OPCode.REQ)) {
final COPSReqMsg rMsg = (COPSReqMsg) rmsg;
return rMsg.getClientHandle();
} else {
sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA, "Received unknown object");
return null;
}
}
} catch (Exception e) {
logger.error("Error COPSTransceiver.receiveMsg", e);
return null;
}
}
/**
* Creates the PDP connection object
* @param conn - the socket connection to the PDP
* @param handle - the client's handle
* @return - the PDP connection object
*/
protected COPSPdpConnection setputPdpConnection(final Socket conn, final COPSHandle handle) {
logger.debug("PDPCOPSConnection");
final COPSPdpConnection pdpConn = new COPSPdpConnection(_pepId, conn, _process, _kaTimer, _acctTimer);
// XXX - handleRequestMsg
// XXX - check handle is valid
final COPSPdpReqStateMan man = new COPSPdpReqStateMan(_clientType, handle, _process, conn);
pdpConn.addStateMan(handle, man);
// XXX - End handleRequestMsg
logger.info("Starting PDP connection thread to - " + _host);
return pdpConn;
}
}