Merge changes If0630105,I9d2d5e61,I1cea2a32,Icc05b6a7,Ic57eb4f8, ...
[packetcable.git] / packetcable-driver / src / main / java / org / umu / cops / stack / COPSDecision.java
1 /*
2  * Copyright (c) 2003 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.stack;
8
9 import org.umu.cops.stack.COPSObjHeader.CNum;
10 import org.umu.cops.stack.COPSObjHeader.CType;
11
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.net.Socket;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17
18 /**
19  * COPS Decision (RFC 2748)
20  *
21  * Decision made by the PDP. Appears in replies. The specific non-
22  * mandatory decision objects required in a decision to a particular
23  * request depend on the type of client.
24  *
25  * C-Num = 6
26  * C-Type = 1, Decision Flags (Mandatory)
27  *
28  * Commands:
29  * 0 = NULL Decision (No configuration data available)
30  * 1 = Install (Admit request/Install configuration)
31  * 2 = Remove (Remove request/Remove configuration)
32  *
33  * Flags:
34  * 0x01 = Trigger Error (Trigger error message if set)
35  * Note: Trigger Error is applicable to client-types that
36  * are capable of sending error notifications for signaled
37  * messages.
38  *
39  * Flag values not applicable to a given context's R-Type or
40  * client-type MUST be ignored by the PEP.
41  *
42  * C-Type = 2, Stateless Data
43  *
44  * This type of decision object carries additional stateless
45  * information that can be applied by the PEP locally. It is a
46  * variable length object and its internal format SHOULD be
47  * specified in the relevant COPS extension document for the given
48  * client-type. This object is optional in Decision messages and is
49  * interpreted relative to a given context.
50  *
51  * It is expected that even outsourcing PEPs will be able to make
52  * some simple stateless policy decisions locally in their LPDP. As
53  * this set is well known and implemented ubiquitously, PDPs are
54  * aware of it as well (either universally, through configuration,
55  * or using the Client-Open message). The PDP may also include this
56  * information in its decision, and the PEP MUST apply it to the
57  * resource allocation event that generated the request.
58  *
59  * C-Type = 3, Replacement Data
60  *
61  * This type of decision object carries replacement data that is to
62  * replace existing data in a signaled message. It is a variable
63  * length object and its internal format SHOULD be specified in the
64  * relevant COPS extension document for the given client-type. It is
65  * optional in Decision messages and is interpreted relative to a
66  * given context.
67  *
68  * C-Type = 4, Client Specific Decision Data
69  *
70  * Additional decision types can be introduced using the Client
71  * Specific Decision Data Object. It is a variable length object and
72  * its internal format SHOULD be specified in the relevant COPS
73  * extension document for the given client-type. It is optional in
74  * Decision messages and is interpreted relative to a given context.
75  *
76  * C-Type = 5, Named Decision Data
77  *
78  * Named configuration information is encapsulated in this version
79  * of the decision object in response to configuration requests. It
80  * is a variable length object and its internal format SHOULD be
81  * specified in the relevant COPS extension document for the given
82  * client-type. It is optional in Decision messages and is
83  * interpreted relative to both a given context and decision flags.
84  */
85 public class COPSDecision extends COPSObjBase {
86
87     static Map<Integer, Command> VAL_TO_CMD = new ConcurrentHashMap<>();
88     static {
89         VAL_TO_CMD.put(Command.NULL.ordinal(), Command.NULL);
90         VAL_TO_CMD.put(Command.INSTALL.ordinal(), Command.INSTALL);
91         VAL_TO_CMD.put(Command.REMOVE.ordinal(), Command.REMOVE);
92     }
93
94     static Map<Integer, DecisionFlag> VAL_TO_FLAG = new ConcurrentHashMap<>();
95     static {
96         VAL_TO_FLAG.put(DecisionFlag.NA.ordinal(), DecisionFlag.NA);
97         VAL_TO_FLAG.put(DecisionFlag.REQERROR.ordinal(), DecisionFlag.REQERROR);
98         VAL_TO_FLAG.put(DecisionFlag.REQSTATE.ordinal(), DecisionFlag.REQSTATE);
99     }
100
101     /**
102      * All CTypes are supported except NA
103      */
104     private final Command _cmdCode;
105     private final DecisionFlag _flags;
106     private final COPSData _data;
107     private final COPSData _padding;
108
109     /**
110      * Constructor generally used for sending messages without any extra data
111      * @param cmdCode - the command
112      * @throws java.lang.IllegalArgumentException
113      */
114     public COPSDecision(final Command cmdCode) {
115         this(CType.DEF, cmdCode, DecisionFlag.NA, null);
116     }
117
118     /**
119      * Constructor generally used for sending messages with a specific CType and extra data and a NA decision flag
120      * @param cType - the CType
121      * @param data - the data
122      * @throws java.lang.IllegalArgumentException
123      */
124     public COPSDecision(final CType cType, final COPSData data) {
125         this(cType, Command.NULL, DecisionFlag.NA, data);
126     }
127
128     /**
129      * Constructor generally used for sending messages with a specific Command and DecisionFlag
130      * @param cmdCode - the command
131      * @param flags - the flags
132      * @throws java.lang.IllegalArgumentException
133      */
134     public COPSDecision(final Command cmdCode, final DecisionFlag flags) {
135         this(CType.DEF, cmdCode, flags, null);
136     }
137
138     /**
139      * Constructor generally used for sending messages with a specific, CType, Command and DecisionFlag
140      * @param cType - the CType
141      * @param cmdCode - the command
142      * @param flags - the flags
143      * @throws java.lang.IllegalArgumentException
144      */
145     public COPSDecision(final CType cType, final Command cmdCode, final DecisionFlag flags) {
146         this(cType, cmdCode, flags, null);
147     }
148
149     /**
150      * Constructor generally used for sending messages with a specific, CType, Command, DecisionFlag and data
151      * @param cType - the CType
152      * @param cmdCode - the command
153      * @param flags - the flags
154      * @param data - the data
155      * @throws java.lang.IllegalArgumentException
156      */
157     public COPSDecision(final CType cType, final Command cmdCode, final DecisionFlag flags,
158                         final COPSData data) {
159         this(new COPSObjHeader(CNum.DEC, cType), cmdCode, flags, data);
160     }
161
162     /**
163      * Constructor generally used when parsing the bytes of an inbound COPS message but can also be used when the
164      * COPSObjHeader information is known
165      * @param hdr - the object header
166      * @param cmdCode - the command
167      * @param flags - the flags
168      * @param data - the data
169      * @throws java.lang.IllegalArgumentException
170      */
171     protected COPSDecision(final COPSObjHeader hdr, final Command cmdCode, final DecisionFlag flags,
172                            final COPSData data) {
173         super(hdr);
174         // TODO - find a better way to make this check
175         if (this.getClass().getName().equals("org.umu.cops.stack.COPSDecision") && !hdr.getCNum().equals(CNum.DEC))
176             throw new IllegalArgumentException("CNum must be equal to " + CNum.DEC);
177
178         if (hdr.getCType().equals(CType.NA)) throw new IllegalArgumentException("CType must not be " + CType.NA);
179         if (cmdCode == null) throw new IllegalArgumentException("Command code must not be null");
180         if (flags == null) throw new IllegalArgumentException("Flags must not be null");
181
182         _cmdCode = cmdCode;
183         _flags = flags;
184
185         if (data == null) _data = new COPSData();
186         else _data = data;
187
188         if ((_data.length() % 4) != 0) {
189             final int padLen = 4 - (_data.length() % 4);
190             _padding = COPSObjectParser.getPadding(padLen);
191         } else {
192             _padding = new COPSData();
193         }
194     }
195
196     /**
197      * Returns the command
198      * @return - the command
199      */
200     public Command getCommand() { return _cmdCode; }
201
202     @Override
203     public int getDataLength() {
204         return 4 + _data.length() + _padding.length();
205     }
206
207     /**
208      * Get the associated data if decision object is of cType 2 or higher
209      * @return   a COPSData
210      */
211     public COPSData getData() {
212         return (_data);
213     }
214
215     /**
216      * If cType == 1 , get the flags associated
217      * @return   a short
218      */
219     public DecisionFlag getFlag() {
220         return _flags;
221     }
222
223     /**
224      * Method getTypeStr
225      * @return   a String
226      */
227     public String getTypeStr() {
228         switch (this.getHeader().getCType()) {
229             case DEF:
230                 return "Default";
231             case STATELESS:
232                 return "Stateless data";
233             case REPL:
234                 return "Replacement data";
235             case CSI:
236                 return "Client specific decision data";
237             case NAMED:
238                 return "Named decision data";
239             default:
240                 return "Unknown";
241         }
242     }
243
244     @Override
245     protected void writeBody(final Socket socket) throws IOException {
246         final byte[] buf = new byte[4];
247         buf[0] = (byte) (_cmdCode.ordinal() >> 8);
248         buf[1] = (byte) _cmdCode.ordinal();
249         buf[2] = (byte) (_flags.ordinal() >> 8);
250         buf[3] = (byte) _flags.ordinal();
251         COPSUtil.writeData(socket, buf, 4);
252
253         COPSUtil.writeData(socket, _data.getData(), _data.length());
254         if (_padding != null) {
255             COPSUtil.writeData(socket, _padding.getData(), _padding.length());
256         }
257     }
258
259     @Override
260     protected void dumpBody(final OutputStream os) throws IOException {
261         if (this.getHeader().getCType().equals(CType.DEF)) {
262             os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
263             os.write(("Command code: " + _cmdCode + "\n").getBytes());
264             os.write(("Command flags: " + _flags + "\n").getBytes());
265         } else {
266             os.write(("Decision (" + getTypeStr() + ")\n").getBytes());
267             os.write(("Data: " + _data.str() + "\n").getBytes());
268         }
269     }
270
271     @Override
272     public boolean equals(final Object o) {
273         if (this == o) {
274             return true;
275         }
276         if (!(o instanceof COPSDecision)) {
277             return false;
278         }
279         if (!super.equals(o)) {
280             return false;
281         }
282
283         final COPSDecision that = (COPSDecision) o;
284
285         return _cmdCode == that._cmdCode && _flags == that._flags && _data.equals(that._data) &&
286                 _padding.equals(that._padding) ||
287                 COPSUtil.copsDataPaddingEquals(this._data, this._padding, that._data, that._padding);
288     }
289
290     @Override
291     public int hashCode() {
292         int result = super.hashCode();
293         result = 31 * result + _data.hashCode();
294         result = 31 * result + _cmdCode.hashCode();
295         result = 31 * result + _flags.hashCode();
296         result = 31 * result + _padding.hashCode();
297         return result;
298     }
299
300     /**
301      * Parses bytes to return a COPSDecision object
302      * @param objHdrData - the associated header
303      * @param dataPtr - the data to parse
304      * @return - the object
305      * @throws java.lang.IllegalArgumentException
306      */
307     public static COPSDecision parse(final COPSObjHeaderData objHdrData, final byte[] dataPtr) {
308         int _cmdCode = 0;
309         _cmdCode |= ((short) dataPtr[4]) << 8;
310         _cmdCode |= ((short) dataPtr[5]) & 0xFF;
311
312         int _flags = 0;
313         _flags |= ((short) dataPtr[6]) << 8;
314         _flags |= ((short) dataPtr[7]) & 0xFF;
315
316         final COPSData d;
317         if (objHdrData.header.getCType().equals(CType.DEF)) {
318             d = null;
319         } else {
320             d = new COPSData(dataPtr, 8, objHdrData.msgByteCount - 8);
321         }
322         return new COPSDecision(objHdrData.header, COPSDecision.VAL_TO_CMD.get(_cmdCode),
323                 COPSDecision.VAL_TO_FLAG.get(_flags), d);
324     }
325
326     /**
327      * Supported command types
328      */
329     public enum Command {
330         NULL,    // No configuration data available
331         INSTALL, // Admit request/install configuration
332         REMOVE   // Remove request/remove configuration
333     }
334
335     public enum DecisionFlag {
336         NA,
337         REQERROR, // = Trigger error
338         REQSTATE, // = ???
339     }
340
341 }