Refactor PCMM aspects of COPS message data objects.
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / prpdp / COPSPdpAgent.java
1 /*
2  * Copyright (c) 2004 University of Murcia.  All rights reserved.
3  * --------------------------------------------------------------
4  * For more information, please see <http://www.umu.euro6ix.org/>.
5  */
6
7 package org.umu.cops.prpdp;
8
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;
15
16 import java.io.IOException;
17 import java.net.InetAddress;
18 import java.net.InetSocketAddress;
19 import java.net.Socket;
20
21 /**
22  * Core PDP agent for managing the connection to one PDP.
23  */
24 public class COPSPdpAgent {
25
26     private static final Logger logger = LoggerFactory.getLogger(COPSPdpAgent.class);
27
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;
34
35     /**
36      * PDP host address
37      */
38     protected final String _host;
39
40     /**
41      * PDP host port
42      */
43     protected final int _serverPort;
44
45     /**
46      * Client-type of connecting PEP
47      */
48     protected final short _clientType;
49
50     /**
51      * Accounting timer (secs)
52      */
53     protected final short _acctTimer;
54
55     /**
56      * Keep-alive timer (secs)
57      */
58     protected final short _kaTimer;
59
60     /**
61      *  Policy data processing object
62      */
63     protected final COPSPdpDataProcess _process;
64
65     // Next two attributes are initialized when connected
66     /**
67      * The Socket connection to the PEP
68      */
69     protected transient Socket _socket;
70
71     /**
72      * The PEP handle
73      */
74     protected transient COPSHandle _handle;
75
76     // Next three attributes are initialized after the client accepts
77     /**
78      * Holds the last PEP ID processed
79      */
80     protected transient COPSPepId _pepId;
81
82     /**
83      * the PDP connection connection
84      */
85     protected transient COPSPdpConnection _pdpConn;
86
87     /**
88      * The handle to the tread accepting messages from the PDP
89      */
90     protected transient Thread _thread;
91
92     /**
93      * Creates a PDP Agent
94      *
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
99      */
100     public COPSPdpAgent(final String host, final int port, final short clientType, final COPSPdpDataProcess process) {
101         this._host = host;
102         this._serverPort = port;
103
104         this._kaTimer = KA_TIMER_VALUE;
105         this._acctTimer = ACCT_TIMER_VALUE;
106
107         this._clientType = clientType;
108         this._process = process;
109     }
110
111     /**
112      * Returns handle after connect() has successfully been executed
113      * @return - the handle
114      */
115     public COPSHandle getClientHandle() {
116         return _handle;
117     }
118
119     /**
120      * Returns handle after connect() has successfully been executed
121      * @return - the handle
122      */
123     public Socket getSocket() {
124         return _socket;
125     }
126
127     /**
128      * Connects to a PDP
129      * @throws java.net.UnknownHostException
130      * @throws java.io.IOException
131      * @throws COPSException
132      */
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);
142         } else {
143             try {
144                 _socket.close();
145             } catch (Exception ex) {
146                 logger.error("Unexpected error closing socket", ex);
147             }
148         }
149     }
150
151     /**
152      * Disconnects a PEP and stops the listener thread
153      * @param error COPS Error to be reported as a reason
154      */
155     public void disconnect(final COPSError error) {
156         if (_pdpConn != null) {
157             sendCloseMessage(_socket, error.getErrCode(), error.getErrSubCode(), "Disconnecting from PDP requested");
158             _pdpConn.close();
159         } else {
160             logger.warn("Unable to locate PDP connection. Cannot close");
161         }
162         if (_thread != null) _thread.interrupt();
163         else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
164
165         if (_socket != null && _socket.isConnected())
166             try {
167                 _socket.close();
168             } catch (IOException e) {
169                 logger.error("Error closing socket", e);
170             }
171
172         _socket = null;
173         _pepId = null;
174         _pdpConn = null;
175         _thread = null;
176     }
177
178     /**
179      * Requests a COPS sync for a PEP
180      * @throws COPSException
181      * @throws COPSPdpException
182      */
183     public void sync() throws COPSException {
184         if (_pdpConn != null) _pdpConn.syncAllRequestState();
185         else logger.warn("Unable to sync, not connected to a PEP");
186     }
187
188     /**
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
195      */
196     protected void handleClientOpenMsg(final Socket conn, final COPSMsg msg) throws COPSException, IOException {
197         logger.info("Processing client open message");
198
199         if (_pepId != null) {
200             throw new COPSException("Connection already opened");
201         }
202
203         final COPSClientOpenMsg cMsg = (COPSClientOpenMsg) msg;
204         _pepId = cMsg.getPepId();
205
206         // Validate Client Type
207         if (msg.getHeader().getClientType() != _clientType) {
208             sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE, ErrorTypes.NA,
209                     "Unsupported client type");
210         }
211
212         // PEPId is mandatory
213         if (_pepId == null) {
214             sendCloseMessage(conn, ErrorTypes.MANDATORY_OBJECT_MISSING, ErrorTypes.NA,
215                     "Mandatory COPS object missing (PEPId)");
216         }
217
218         // TODO - Determine if I should be checking for the PDPAddress and Integrity objects on the message too???
219         // Support
220 /*
221         if ( (cMsg.getClientSI() != null) || (cMsg.getPdpAddress() != null) || (cMsg.getIntegrity() != null)) {
222             sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE,
223                     "Unsupported objects (ClientSI, PdpAddress, Integrity)");
224         }
225 */
226         // Support
227         if ((cMsg.getClientSI() == null) ) {
228             sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA,
229                     "Unsupported objects (PdpAddress, Integrity)");
230         } else {
231             final MMVersionInfo _mminfo = MMVersionInfo.parse(cMsg.getClientSI().getData().getData());
232             logger.debug("CMTS sent MMVersion info : major:" + _mminfo.getMajorVersionNB() + "  minor:" +
233                     _mminfo.getMinorVersionNB());
234         }
235
236         acceptConnection(conn);
237
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());
243             _thread.start();
244         } else {
245             throw new COPSException("Unable to connect to PDP");
246         }
247     }
248
249     /**
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
254      */
255     private void sendCloseMessage(final Socket conn, final ErrorTypes errorType, final ErrorTypes errorSubType,
256                                   final String msg) {
257         final COPSClientCloseMsg closeMsg = new COPSClientCloseMsg(_clientType,
258                 new COPSError(errorType, errorSubType), null, null);
259         try {
260             logger.info("Sending client-close message. Reason: " + msg);
261             closeMsg.writeData(conn);
262         } catch (IOException unae) {
263             logger.error("Exception writing data", unae);
264         }
265     }
266
267     /**
268      * Sends a client-accept message back to the PDP
269      * @param conn - the socket connection to the PDP
270      * @throws IOException
271      */
272     private void acceptConnection(final Socket conn) throws IOException {
273         final COPSClientAcceptMsg acceptMsg;
274         if (_acctTimer != 0)
275             acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer),
276                     new COPSAcctTimer(_acctTimer), null);
277         else
278             acceptMsg = new COPSClientAcceptMsg(_clientType, new COPSKATimer(_kaTimer) ,null, null);
279         acceptMsg.writeData(conn);
280     }
281
282     /**
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
287      */
288     private COPSHandle handleAcceptResponse(final Socket conn) {
289         try {
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());
293             // Client-Close
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");
297                 return null;
298             } else {
299                 // Request
300                 if (rmsg.getHeader().getOpCode().equals(OPCode.REQ)) {
301                     final COPSReqMsg rMsg = (COPSReqMsg) rmsg;
302                     return rMsg.getClientHandle();
303                 } else {
304                     sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA, "Received unknown object");
305                     return null;
306                 }
307             }
308         } catch (Exception e) {
309             logger.error("Error COPSTransceiver.receiveMsg", e);
310             return null;
311         }
312     }
313
314     /**
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
319      */
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);
323
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
329
330         logger.info("Starting PDP connection thread to - " + _host);
331         return pdpConn;
332     }
333
334 }
335
336
337