637d84d22e2ee31910c610d15689ae3c8266974b
[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.Socket;
19
20 /**
21  * Core PDP agent for managing the connection to one PDP.
22  */
23 public class COPSPdpAgent {
24
25     private static final Logger logger = LoggerFactory.getLogger(COPSPdpAgent.class);
26
27     /** Well-known port for COPS */
28     public static final int WELL_KNOWN_PDP_PORT = 3288;
29     /** Default keep-alive timer value (secs) */
30     public static final short KA_TIMER_VALUE = 30;
31     /** Default accounting timer value (secs) */
32     public static final short ACCT_TIMER_VALUE = 0;
33
34     /**
35      * PDP host address
36      */
37     protected final String _host;
38
39     /**
40      * PDP host port
41      */
42     protected final int _serverPort;
43
44     /**
45      * Client-type of connecting PEP
46      */
47     protected final short _clientType;
48
49     /**
50      * Accounting timer (secs)
51      */
52     protected final short _acctTimer;
53
54     /**
55      * Keep-alive timer (secs)
56      */
57     protected final short _kaTimer;
58
59     /**
60      *  Policy data processing object
61      */
62     protected final COPSPdpDataProcess _process;
63
64     // Next two attributes are initialized when connected
65     /**
66      * The Socket connection to the PEP
67      */
68     protected transient Socket _socket;
69
70     /**
71      * The PEP handle
72      */
73     protected transient COPSHandle _handle;
74
75     // Next three attributes are initialized after the client accepts
76     /**
77      * Holds the last PEP ID processed
78      */
79     protected transient COPSPepId _pepId;
80
81     /**
82      * the PDP connection connection
83      */
84     protected transient COPSPdpConnection _pdpConn;
85
86     /**
87      * The handle to the tread accepting messages from the PDP
88      */
89     protected transient Thread _thread;
90
91     /**
92      * Creates a PDP Agent
93      *
94      * @param host  PDP agent host name
95      * @param port  Port to listen to
96      * @param clientType    COPS Client-type
97      * @param process   Object to perform policy data processing
98      */
99     public COPSPdpAgent(final String host, final int port, final short clientType, final COPSPdpDataProcess process) {
100         this._host = host;
101         this._serverPort = port;
102
103         this._kaTimer = KA_TIMER_VALUE;
104         this._acctTimer = ACCT_TIMER_VALUE;
105
106         this._clientType = clientType;
107         this._process = process;
108     }
109
110     /**
111      * Returns handle after connect() has successfully been executed
112      * @return - the handle
113      */
114     public COPSHandle getClientHandle() {
115         return _handle;
116     }
117
118     /**
119      * Returns handle after connect() has successfully been executed
120      * @return - the handle
121      */
122     public Socket getSocket() {
123         return _socket;
124     }
125
126     /**
127      * Connects to a PDP
128      * @throws java.net.UnknownHostException
129      * @throws java.io.IOException
130      * @throws COPSException
131      */
132     public void connect() throws IOException, COPSException {
133         // Create Socket and send OPN
134         final InetAddress addr = InetAddress.getByName(_host);
135         _socket = new Socket(addr, _serverPort);
136         logger.info("PDP Socket Opened. Waiting to receive client-open message");
137         final COPSMsg msg = COPSTransceiver.receiveMsg(_socket);
138         logger.debug("Message received of type - " + msg.getHeader().getOpCode());
139         if (msg.getHeader().getOpCode().equals(OPCode.OPN)) {
140             handleClientOpenMsg(_socket, msg);
141         } else {
142             try {
143                 _socket.close();
144             } catch (Exception ex) {
145                 logger.error("Unexpected error closing socket", ex);
146             }
147         }
148     }
149
150     /**
151      * Disconnects a PEP and stops the listener thread
152      * @param error COPS Error to be reported as a reason
153      */
154     public void disconnect(final COPSError error) {
155         if (_pdpConn != null) {
156             sendCloseMessage(_socket, error.getErrCode(), error.getErrSubCode(), "Disconnecting from PDP requested");
157             _pdpConn.close();
158         } else {
159             logger.warn("Unable to locate PDP connection. Cannot close");
160         }
161         if (_thread != null) _thread.interrupt();
162         else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
163
164         if (_socket.isConnected())
165             try {
166                 _socket.close();
167             } catch (IOException e) {
168                 logger.error("Error closing socket", e);
169             }
170
171         _socket = null;
172         _pepId = null;
173         _pdpConn = null;
174         _thread = null;
175     }
176
177     /**
178      * Requests a COPS sync for a PEP
179      * @throws COPSException
180      * @throws COPSPdpException
181      */
182     public void sync() throws COPSException {
183         if (_pdpConn != null) _pdpConn.syncAllRequestState();
184         else logger.warn("Unable to sync, not connected to a PEP");
185     }
186
187     /**
188      * Handles a COPS client-open message and sets the _pepId, _handle, _pdpConn, & _thread objects in the process
189      * as well as starts the PDP connection thread for receiving other COPS messages from the PDP
190      * @param    conn Socket to the PEP
191      * @param    msg <tt>COPSMsg</tt> holding the client-open message
192      * @throws COPSException
193      * @throws IOException
194      */
195     protected void handleClientOpenMsg(final Socket conn, final COPSMsg msg) throws COPSException, IOException {
196         logger.info("Processing client open message");
197
198         if (_pepId != null) {
199             throw new COPSException("Connection already opened");
200         }
201
202         final COPSClientOpenMsg cMsg = (COPSClientOpenMsg) msg;
203         _pepId = cMsg.getPepId();
204
205         // Validate Client Type
206         if (msg.getHeader().getClientType() != _clientType) {
207             sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE, ErrorTypes.NA,
208                     "Unsupported client type");
209         }
210
211         // PEPId is mandatory
212         if (_pepId == null) {
213             sendCloseMessage(conn, ErrorTypes.MANDATORY_OBJECT_MISSING, ErrorTypes.NA,
214                     "Mandatory COPS object missing (PEPId)");
215         }
216
217         // TODO - Determine if I should be checking for the PDPAddress and Integrity objects on the message too???
218         // Support
219 /*
220         if ( (cMsg.getClientSI() != null) || (cMsg.getPdpAddress() != null) || (cMsg.getIntegrity() != null)) {
221             sendCloseMessage(conn, ErrorTypes.UNSUPPORTED_CLIENT_TYPE,
222                     "Unsupported objects (ClientSI, PdpAddress, Integrity)");
223         }
224 */
225         // Support
226         if ((cMsg.getClientSI() == null) ) {
227             sendCloseMessage(conn, ErrorTypes.UNKNOWN_OBJECT, ErrorTypes.NA,
228                     "Unsupported objects (PdpAddress, Integrity)");
229         } else {
230             final MMVersionInfo _mminfo = new MMVersionInfo(cMsg.getClientSI().getData().getData());
231             logger.debug("CMTS sent MMVersion info : major:" + _mminfo.getMajorVersionNB() + "  minor:" +
232                     _mminfo.getMinorVersionNB());
233         }
234
235         acceptConnection(conn);
236
237         _handle = handleAcceptResponse(conn);
238         if (_handle != null) {
239             // Connection accepted
240             _pdpConn = setputPdpConnection(conn, _handle);
241             _thread = new Thread(_pdpConn, "PDP Agent for PEP ID " + _pepId.getData().str());
242             _thread.start();
243         } else {
244             throw new COPSException("Unable to connect to PDP");
245         }
246     }
247
248     /**
249      * Creates and sends a client close message
250      * @param conn - the socket connection
251      * @param errorType - the error type to send
252      * @param msg - the error message to log
253      * @throws COPSException
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);
327         pdpConn.addStateMan(handle, man);
328         try {
329             man.initRequestState(conn);
330         } catch (COPSException unae) {
331             logger.error("Unexpected error initializing state", unae);
332         }
333         // XXX - End handleRequestMsg
334
335         logger.info("Starting PDP connection thread to - " + _host);
336         return pdpConn;
337     }
338
339 }
340
341
342