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