001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.transport.tcp;
018    
019    import java.net.Socket;
020    import java.net.SocketException;
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    /**
025     * Utilities for determining the values for the bits in the headers of the
026     * outgoing TCP/IP packets that indicate Traffic Class for use in Quality of
027     * Service forwarding policies.
028     */
029    public class QualityOfServiceUtils {
030    
031        private static final int MAX_DIFF_SERV = 63;
032        private static final int MIN_DIFF_SERV = 0;
033        private static final Map<String, Integer> DIFF_SERV_NAMES
034            = new HashMap<String, Integer>();
035        /** Common names used for Differentiated Services values. */
036        static {
037            
038            DIFF_SERV_NAMES.put("CS0", 0);
039            DIFF_SERV_NAMES.put("CS1", 8);
040            DIFF_SERV_NAMES.put("CS2", 16);
041            DIFF_SERV_NAMES.put("CS3", 24);
042            DIFF_SERV_NAMES.put("CS4", 32);
043            DIFF_SERV_NAMES.put("CS5", 40);
044            DIFF_SERV_NAMES.put("CS6", 48);
045            DIFF_SERV_NAMES.put("CS7", 56);
046            DIFF_SERV_NAMES.put("AF11", 10);
047            DIFF_SERV_NAMES.put("AF12", 12);
048            DIFF_SERV_NAMES.put("AF13", 14);
049            DIFF_SERV_NAMES.put("AF21", 18);
050            DIFF_SERV_NAMES.put("AF22", 20);
051            DIFF_SERV_NAMES.put("AF23", 22);
052            DIFF_SERV_NAMES.put("AF31", 26);
053            DIFF_SERV_NAMES.put("AF32", 28);
054            DIFF_SERV_NAMES.put("AF33", 30);
055            DIFF_SERV_NAMES.put("AF41", 34);
056            DIFF_SERV_NAMES.put("AF42", 36);
057            DIFF_SERV_NAMES.put("AF43", 38);
058            DIFF_SERV_NAMES.put("EF", 46);
059        }
060    
061        private static final int MAX_TOS = 255;
062        private static final int MIN_TOS = 0;
063    
064        /**
065         * @param value A potential value to be used for Differentiated Services.
066         * @return The corresponding Differentiated Services Code Point (DSCP).
067         * @throws IllegalArgumentException if the value does not correspond to a
068         *         Differentiated Services Code Point or setting the DSCP is not
069         *         supported.
070         */
071        public static int getDSCP(String value) throws IllegalArgumentException {
072            int intValue = -1;
073    
074            // Check the names first.
075            if (DIFF_SERV_NAMES.containsKey(value)) {
076                intValue = DIFF_SERV_NAMES.get(value);
077            } else {
078                try {
079                    intValue = Integer.parseInt(value);
080                    if (intValue > MAX_DIFF_SERV || intValue < MIN_DIFF_SERV) {
081                        throw new IllegalArgumentException("Differentiated Services"
082                            + " value: " + intValue + " not in legal range ["
083                            + MIN_DIFF_SERV + ", " + MAX_DIFF_SERV + "].");
084                    }
085                } catch (NumberFormatException e) {
086                    // value must have been a malformed name.
087                    throw new IllegalArgumentException("No such Differentiated "
088                        + "Services name: " + value);
089                }
090            }
091    
092            return adjustDSCPForECN(intValue);
093         }
094    
095    
096        /**
097         * @param value A potential value to be used for Type of Service.
098         * @return A valid value that can be used to set the Type of Service in the
099         *         packet headers.
100         * @throws IllegalArgumentException if the value is not a legal Type of
101         *         Service value.
102         */
103        public static int getToS(int value) throws IllegalArgumentException {
104            if (value > MAX_TOS || value < MIN_TOS) {
105                throw new IllegalArgumentException("Type of Service value: "
106                    + value + " not in legal range [" + MIN_TOS + ", " + MAX_TOS
107                    + ".");
108            }
109            return value;
110        }
111    
112        /**
113         * The Differentiated Services values use only 6 of the 8 bits in the field
114         * in the TCP/IP packet header. Make sure any values the system has set for
115         * the other two bits (the ECN bits) are maintained.
116         *
117         * @param dscp The Differentiated Services Code Point.
118         * @return A Differentiated Services Code Point that respects the ECN bits
119         *         set on the system.
120         * @throws IllegalArgumentException if setting Differentiated Services is
121         *         not supported.
122         */
123        private static int adjustDSCPForECN(int dscp)
124                throws IllegalArgumentException {
125            // The only way to see if there are any values set for the ECN is to
126            // read the traffic class automatically set by the system and isolate
127            // the ECN bits.
128            Socket socket = new Socket();
129            try {
130                int systemTrafficClass = socket.getTrafficClass();
131                // The 1st and 2nd bits of the system traffic class are the ECN
132                // bits.
133                return (dscp << 2) | (systemTrafficClass & 3);
134            } catch (SocketException e) {
135                throw new IllegalArgumentException("Setting Differentiated Services"
136                    + " not supported: " + e);
137            }
138        }
139    }