blob: 2d3638c1e4272283f02d2b516da73f3e6eb5fd57 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070019package org.onlab.packet;
20
21import java.util.Arrays;
22
23/**
Jonathan Hartdec62d42014-09-22 15:59:04 -070024 * A class representing an IPv4 prefix.
25 * <p/>
26 * A prefix consists of an IP address and a subnet mask.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070027 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070028public final class IpPrefix {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070029
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -070030 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
31
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070032 //IP Versions
33 public enum Version { INET, INET6 };
34
Ayaka Koshibe04a1a4e2014-09-11 14:31:29 -070035 //lengths of address, in bytes
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070036 public static final int INET_LEN = 4;
Ayaka Koshibe3a25aec2014-09-12 11:52:53 -070037 public static final int INET6_LEN = 16;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070038
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070039 //maximum CIDR value
40 public static final int MAX_INET_MASK = 32;
Ayaka Koshibeb55524f2014-09-18 09:59:24 -070041 //no mask (no network), e.g. a simple address
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070042 public static final int DEFAULT_MASK = 0;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070043
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070044 /**
45 * Default value indicating an unspecified address.
46 */
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070047 static final byte[] ANY = new byte [] {0, 0, 0, 0};
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070048
49 protected Version version;
50
51 protected byte[] octets;
52 protected int netmask;
53
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070054 private IpPrefix(Version ver, byte[] octets, int netmask) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070055 this.version = ver;
56 this.octets = Arrays.copyOf(octets, INET_LEN);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070057 this.netmask = netmask;
58 }
59
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070060 private IpPrefix(Version ver, byte[] octets) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070061 this.version = ver;
62 this.octets = Arrays.copyOf(octets, INET_LEN);
63 this.netmask = DEFAULT_MASK;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070064 }
65
66 /**
67 * Converts a byte array into an IP address.
68 *
69 * @param address a byte array
70 * @return an IP address
71 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070072 public static IpPrefix valueOf(byte [] address) {
73 return new IpPrefix(Version.INET, address);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070074 }
75
76 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070077 * Converts a byte array into an IP address.
78 *
79 * @param address a byte array
80 * @param netmask the CIDR value subnet mask
81 * @return an IP address
82 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070083 public static IpPrefix valueOf(byte [] address, int netmask) {
84 return new IpPrefix(Version.INET, address, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070085 }
86
87 /**
88 * Helper to convert an integer into a byte array.
89 *
90 * @param address the integer to convert
91 * @return a byte array
92 */
93 private static byte [] bytes(int address) {
94 byte [] bytes = new byte [INET_LEN];
95 for (int i = 0; i < INET_LEN; i++) {
96 bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff);
97 }
98
99 return bytes;
100 }
101
102 /**
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700103 * Converts an integer into an IPv4 address.
104 *
105 * @param address an integer representing an IP value
106 * @return an IP address
107 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700108 public static IpPrefix valueOf(int address) {
109 return new IpPrefix(Version.INET, bytes(address));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700110 }
111
112 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700113 * Converts an integer into an IPv4 address.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700114 *
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700115 * @param address an integer representing an IP value
116 * @param netmask the CIDR value subnet mask
117 * @return an IP address
118 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700119 public static IpPrefix valueOf(int address, int netmask) {
120 return new IpPrefix(Version.INET, bytes(address), netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700121 }
122
123 /**
124 * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -0700125 * string can also be in CIDR (slash) notation. If the netmask is omitted,
126 * it will be set to DEFAULT_MASK (0).
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700127 *
128 * @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24"
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700129 * @return an IP address
130 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700131 public static IpPrefix valueOf(String address) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700132
133 final String [] parts = address.split("\\/");
134 if (parts.length > 2) {
135 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700136 + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700137 }
138
139 int mask = DEFAULT_MASK;
140 if (parts.length == 2) {
Yuta HIGUCHI7f3df232014-10-15 23:38:24 -0700141 mask = Integer.parseInt(parts[1]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700142 if (mask > MAX_INET_MASK) {
143 throw new IllegalArgumentException(
144 "Value of subnet mask cannot exceed "
alshabib8f1cf4a2014-09-17 14:44:48 -0700145 + MAX_INET_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700146 }
147 }
148
149 final String [] net = parts[0].split("\\.");
150 if (net.length != INET_LEN) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700151 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700152 + "Address must have four decimal values separated by dots (.)");
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700153 }
154 final byte [] bytes = new byte[INET_LEN];
155 for (int i = 0; i < INET_LEN; i++) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700156 bytes[i] = (byte) Short.parseShort(net[i], 10);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700157 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700158 return new IpPrefix(Version.INET, bytes, mask);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700159 }
160
161 /**
162 * Returns the IP version of this address.
163 *
164 * @return the version
165 */
166 public Version version() {
167 return this.version;
168 }
169
170 /**
171 * Returns the IP address as a byte array.
172 *
173 * @return a byte array
174 */
Yuta HIGUCHI10681f62014-09-21 17:49:46 -0700175 public byte[] toOctets() {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700176 return Arrays.copyOf(this.octets, INET_LEN);
177 }
178
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700179 /**
Yuta HIGUCHI10681f62014-09-21 17:49:46 -0700180 * Returns the IP address prefix length.
181 *
182 * @return prefix length
183 */
184 public int prefixLength() {
185 return netmask;
186 }
187
188 /**
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700189 * Returns the integral value of this IP address.
190 *
191 * @return the IP address's value as an integer
192 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700193 public int toInt() {
alshabib6eb438a2014-10-01 16:39:37 -0700194 int val = 0;
195 for (int i = 0; i < octets.length; i++) {
196 val <<= 8;
197 val |= octets[i] & 0xff;
198 }
199 return val;
200 }
201
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700202 /**
203 * Helper for computing the mask value from CIDR.
204 *
205 * @return an integer bitmask
206 */
207 private int mask() {
208 int shift = MAX_INET_MASK - this.netmask;
209 return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
210 }
211
212 /**
213 * Returns the subnet mask in IpAddress form. The netmask value for
214 * the returned IpAddress is 0, as the address itself is a mask.
215 *
216 * @return the subnet mask
217 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700218 public IpPrefix netmask() {
219 return new IpPrefix(Version.INET, bytes(mask()));
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700220 }
221
222 /**
223 * Returns the network portion of this address as an IpAddress.
224 * The netmask of the returned IpAddress is the current mask. If this
225 * address doesn't have a mask, this returns an all-0 IpAddress.
226 *
227 * @return the network address or null
228 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700229 public IpPrefix network() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700230 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700231 return new IpPrefix(version, ANY, DEFAULT_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700232 }
233
234 byte [] net = new byte [4];
235 byte [] mask = bytes(mask());
236 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700237 net[i] = (byte) (octets[i] & mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700238 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700239 return new IpPrefix(version, net, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700240 }
241
242 /**
243 * Returns the host portion of the IPAddress, as an IPAddress.
244 * The netmask of the returned IpAddress is the current mask. If this
245 * address doesn't have a mask, this returns a copy of the current
246 * address.
247 *
248 * @return the host address
249 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700250 public IpPrefix host() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700251 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700252 new IpPrefix(version, octets, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700253 }
254
255 byte [] host = new byte [INET_LEN];
256 byte [] mask = bytes(mask());
257 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700258 host[i] = (byte) (octets[i] & ~mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700259 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700260 return new IpPrefix(version, host, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700261 }
262
Jonathan Hart70da5122014-10-01 16:37:42 -0700263 /**
264 * Returns an IpAddress of the bytes contained in this prefix.
265 * FIXME this is a hack for now and only works because IpPrefix doesn't
266 * mask the input bytes on creation.
267 *
268 * @return the IpAddress
269 */
270 public IpAddress toIpAddress() {
271 return IpAddress.valueOf(octets);
272 }
273
alshabib8f1cf4a2014-09-17 14:44:48 -0700274 public boolean isMasked() {
275 return mask() != 0;
276 }
277
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700278 /**
279 * Determines whether a given address is contained within this IpAddress'
280 * network.
281 *
282 * @param other another IP address that could be contained in this network
283 * @return true if the other IP address is contained in this address'
284 * network, otherwise false
285 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700286 public boolean contains(IpPrefix other) {
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700287 if (this.netmask <= other.netmask) {
288 // Special case where they're both /32 addresses
289 if (this.netmask == MAX_INET_MASK) {
290 return Arrays.equals(octets, other.octets);
291 }
292
293 // Mask the other address with our network mask
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700294 IpPrefix otherMasked =
295 IpPrefix.valueOf(other.octets, netmask).network();
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700296
297 return network().equals(otherMasked);
298 }
299 return false;
300 }
301
Jonathan Hart70da5122014-10-01 16:37:42 -0700302 public boolean contains(IpAddress address) {
303 // Need to get the network address because prefixes aren't automatically
304 // masked on creation
305 IpPrefix meMasked = network();
306
307 IpPrefix otherMasked =
308 IpPrefix.valueOf(address.octets, netmask).network();
309
310 return Arrays.equals(meMasked.octets, otherMasked.octets);
311 }
312
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700313 @Override
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700314 public int hashCode() {
315 final int prime = 31;
316 int result = 1;
317 result = prime * result + netmask;
318 result = prime * result + Arrays.hashCode(octets);
319 result = prime * result + ((version == null) ? 0 : version.hashCode());
320 return result;
321 }
322
323 @Override
324 public boolean equals(Object obj) {
325 if (this == obj) {
326 return true;
327 }
328 if (obj == null) {
329 return false;
330 }
331 if (getClass() != obj.getClass()) {
332 return false;
333 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700334 IpPrefix other = (IpPrefix) obj;
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700335 if (netmask != other.netmask) {
336 return false;
337 }
Jonathan Hart70da5122014-10-01 16:37:42 -0700338 // TODO not quite right until we mask the input
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700339 if (!Arrays.equals(octets, other.octets)) {
340 return false;
341 }
342 if (version != other.version) {
343 return false;
344 }
345 return true;
346 }
347
348 @Override
349 /*
350 * (non-Javadoc)
351 * format is "x.x.x.x" for non-masked (netmask 0) addresses,
352 * and "x.x.x.x/y" for masked addresses.
353 *
354 * @see java.lang.Object#toString()
355 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700356 public String toString() {
357 final StringBuilder builder = new StringBuilder();
358 for (final byte b : this.octets) {
359 if (builder.length() > 0) {
360 builder.append(".");
361 }
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700362 builder.append(String.format("%d", b & 0xff));
363 }
364 if (netmask != DEFAULT_MASK) {
365 builder.append("/");
366 builder.append(String.format("%d", netmask));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700367 }
368 return builder.toString();
369 }
370
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700371}