Added response data to operational datastore, refactored data validation
[packetcable.git] / packetcable-policy-server / src / main / java / org / opendaylight / controller / packetcable / provider / Subnet.java
1 /*
2  * Copyright (c) 2015 CableLabs and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.packetcable.provider;
10
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static com.google.common.base.Preconditions.checkNotNull;
13
14 import java.math.BigInteger;
15 import java.net.Inet4Address;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20 import org.apache.commons.lang3.builder.HashCodeBuilder;
21
22 /**
23  * @author c3oe.de, based on snippets from Scott Plante, John Kugelmann
24  */
25 public class Subnet
26 {
27     /** Minimum length of a v4 or v6 subnet mask */
28     private static final int MIN_MASK_BITS = 0;
29
30     /** Maximum length of a v4 subnet mask */
31     private static final int MAX_MASK_BITS_V4 = 32;
32
33     /** Maximum length of a v6 subnet mask */
34     private static final int MAX_MASK_BITS_V6 = 128;
35
36     /** The length of the subnet prefix */
37     private final int prefixLen;
38
39     /** The subnet mask */
40     private final BigInteger mask;
41
42     /** The actual routing prefix leading to this subnet */
43     private final BigInteger routingPrefix;
44
45     /** The number of bytes in an address. Will be 4 or 16 corresponding to ipv4 and ipv6 respectively */
46     private final int addressByteCount;
47
48     /**
49      * Generates a Subnet from CIDR style notation. <br>
50      * Eg. "192.168.0.0/24" or "2001:db8:85a3:880:0:0:0:0/57"
51      * @param subnetAddress An address in this subnet or the routing prefix leading to this subnet.
52      * @param prefixLength The number of prefix bits that are set, must be between [0..32] for ipv4 and [0..128] for ipv6
53      * @throws NullPointerException if the subnetAddress is null
54      * @throws IllegalArgumentException if the bits argument is not in the allowable range.
55      * @see <a href="http://wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation">http://wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation</a>
56      */
57     public Subnet(@Nonnull final InetAddress subnetAddress, final int prefixLength )
58     {
59         checkNotNull(subnetAddress, "subnetAddress can not be null");
60         final int maxMaskBits = (subnetAddress instanceof Inet4Address ? MAX_MASK_BITS_V4 : MAX_MASK_BITS_V6);
61         checkArgument(prefixLength >= MIN_MASK_BITS && prefixLength <= maxMaskBits, "The prefixLength must be in range [%s..%s] but was %s", MIN_MASK_BITS, maxMaskBits, prefixLength);
62
63         this.prefixLen = prefixLength;
64         this.addressByteCount = subnetAddress.getAddress().length; // 4 or 16
65         this.mask = BigInteger.valueOf( -1 ).shiftLeft(this.addressByteCount * 8 - prefixLength);
66
67         // ensure subnetAddress is properly masked and is not an address in the subnet
68         this.routingPrefix = new BigInteger( subnetAddress.getAddress() ).and( this.mask);
69     }
70
71     /**
72      * Generates a subnet from an address and subnet mask. The old ipv4 way.
73      * Eg: "192.168.0.0/255.255.255.0" or single address with no subnet
74      * @param subnetAddress An address in the subnet or the routing prefix leading to this subnet.
75      * @param subnetMask The subnet mask or null. If null the subnet will be a host identifier.
76      * */
77     public Subnet(@Nonnull final InetAddress subnetAddress, @Nullable final InetAddress subnetMask )
78     {
79         this(subnetAddress, maskToPrefixLen(subnetAddress, subnetMask));
80     }
81
82     /**
83      * Helper method that computes the prefix length an address and subnet mask pair.
84      * @param subnetAddress An address in the subnet
85      * @param subnetMask The mask or null. Expected to be CIDR compliant (mask is a continuous prefix).
86      *                   If null this will return the length of a host identifier.
87      * @return The prefix length
88      * @throws NullPointerException if subnetAddress is null
89      * @throws IllegalArgumentException if subnetMask is not null and
90      *              either (subnetAddress and subnetMask are not the same type)
91      *              or (if subnetMask is not CIDR compliant).
92      */
93     private static int maskToPrefixLen(@Nonnull final InetAddress subnetAddress, @Nullable final InetAddress subnetMask)
94     {
95         checkNotNull(subnetAddress, "subnetAddress can not be null");
96         if (subnetMask == null) {
97             return (subnetAddress instanceof Inet4Address ? MAX_MASK_BITS_V4 : MAX_MASK_BITS_V6);
98         }
99         else {
100             // address and mask must both be ipv4 or ipv6
101             checkArgument(subnetAddress.getClass().equals(subnetMask.getClass()));
102
103             // validate subnet mask. All leading bits should be set
104             final BigInteger m = new BigInteger(subnetMask.getAddress());
105             final BigInteger allOnes = BigInteger.valueOf(-1);
106             final int rightMostBit = m.getLowestSetBit();
107             final BigInteger validSubnet = allOnes.shiftLeft(rightMostBit);
108
109             checkArgument(validSubnet.equals(m), "Subnet should have contiguous prefix bits, mask: %s(%s)", subnetMask);
110
111             return (subnetAddress instanceof Inet4Address
112                     ? MAX_MASK_BITS_V4 - rightMostBit
113                     : MAX_MASK_BITS_V6 - rightMostBit);
114         }
115     }
116
117     /**
118      * Subnet factory method.
119      * @param addressAndSubnetStr format: "192.168.0.0/24" or "192.168.0.0/255.255.255.0"
120      *      or single address or "2001:db8:85a3:880:0:0:0:0/57"
121      * @return a new instance
122      * @throws UnknownHostException thrown if unsupported subnet mask.
123      */
124     public static Subnet createInstance(@Nonnull final String addressAndSubnetStr )
125             throws UnknownHostException
126     {
127         final String[] stringArr = addressAndSubnetStr.split("/");
128         if ( 2 > stringArr.length ) {
129             return new Subnet(InetAddress.getByName(stringArr[0]), null);
130         }
131         else if ( stringArr[ 1 ].contains(".") || stringArr[ 1 ].contains(":") ) {
132             return new Subnet(InetAddress.getByName(stringArr[0]), InetAddress.getByName(stringArr[1]));
133         }
134         else {
135             return new Subnet(InetAddress.getByName(stringArr[0]), Integer.parseInt(stringArr[1]));
136         }
137     }
138
139     /**
140      * Returns the length of the routing prefix length.
141      * @return the routing prefix length.
142      */
143     public int getPrefixLen() {
144                 return prefixLen;
145         }
146
147     /**
148      * Determins if the passed in address is contained in this subnet.
149      * @param address The address to test.
150      * @return true if the address is in this subnet.
151      */
152         public boolean isInNet(@Nonnull final InetAddress address )
153     {
154         checkNotNull(address, "address must not be null");
155
156         final byte[] bytesAddress = address.getAddress();
157         if ( this.addressByteCount != bytesAddress.length ) {
158             return false;
159         }
160         final BigInteger bigAddress = new BigInteger( bytesAddress );
161         return bigAddress.and(this.mask).equals(this.routingPrefix);
162     }
163
164     @Override
165     public final boolean equals( Object obj )
166     {
167         if (null == obj) return false;
168         if (this == obj) return true;
169         if (!(obj instanceof Subnet)) return false;
170
171         final Subnet other = (Subnet)obj;
172         return  this.prefixLen == other.prefixLen &&
173                 this.routingPrefix.equals(other.routingPrefix) &&
174                 this.mask.equals(other.mask) &&
175                 this.addressByteCount == other.addressByteCount;
176     }
177
178     @Override
179     public final int hashCode()
180     {
181         return new HashCodeBuilder(997, 311)
182                 .append(prefixLen)
183                 .append(mask)
184                 .append(routingPrefix)
185                 .append(addressByteCount)
186                 .build();
187     }
188
189     @Override
190     public String toString()
191     {
192         final StringBuilder buf = new StringBuilder();
193         bigInteger2IpString( buf, this.routingPrefix, this.addressByteCount);
194         buf.append( '/' );
195         bigInteger2IpString( buf, this.mask, this.addressByteCount);
196         return buf.toString();
197     }
198
199     private static void bigInteger2IpString( final StringBuilder buf, final BigInteger bigInteger, final int displayBytes )
200     {
201         final boolean isIPv4 = 4 == displayBytes;
202         byte[] bytes = bigInteger.toByteArray();
203         int diffLen = displayBytes - bytes.length;
204         final byte fillByte = 0 > (int)bytes[ 0 ] ? (byte)0xFF : (byte)0x00;
205
206         int integer;
207         for ( int i = 0; i < displayBytes; i++ )
208         {
209             if ( 0 < i && ! isIPv4 && i % 2 == 0 ) {
210                 buf.append(':');
211             }
212             else if ( 0 < i && isIPv4 ) {
213                 buf.append('.');
214             }
215             integer = 0xFF & (i < diffLen ? fillByte : bytes[ i - diffLen ]);
216             if ( ! isIPv4 && 0x10 > integer ) {
217                 buf.append('0');
218             }
219             buf.append( isIPv4 ? integer : Integer.toHexString( integer ) );
220         }
221     }
222 }