blob: 6e1ebb526d39d05a29bc6b9b1158518b44cd49aa [file] [log] [blame]
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -07001package org.onlab.packet;
2
3import java.util.Arrays;
4
5/**
Jonathan Hartdec62d42014-09-22 15:59:04 -07006 * A class representing an IPv4 prefix.
7 * <p/>
8 * A prefix consists of an IP address and a subnet mask.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -07009 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070010public final class IpPrefix {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070011
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -070012 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
13
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070014 //IP Versions
15 public enum Version { INET, INET6 };
16
Ayaka Koshibe04a1a4e2014-09-11 14:31:29 -070017 //lengths of address, in bytes
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070018 public static final int INET_LEN = 4;
Ayaka Koshibe3a25aec2014-09-12 11:52:53 -070019 public static final int INET6_LEN = 16;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070020
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070021 //maximum CIDR value
22 public static final int MAX_INET_MASK = 32;
Ayaka Koshibeb55524f2014-09-18 09:59:24 -070023 //no mask (no network), e.g. a simple address
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070024 public static final int DEFAULT_MASK = 0;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070025
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070026 /**
27 * Default value indicating an unspecified address.
28 */
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070029 static final byte[] ANY = new byte [] {0, 0, 0, 0};
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070030
31 protected Version version;
32
33 protected byte[] octets;
34 protected int netmask;
35
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070036 private IpPrefix(Version ver, byte[] octets, int netmask) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070037 this.version = ver;
38 this.octets = Arrays.copyOf(octets, INET_LEN);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070039 this.netmask = netmask;
40 }
41
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070042 private IpPrefix(Version ver, byte[] octets) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070043 this.version = ver;
44 this.octets = Arrays.copyOf(octets, INET_LEN);
45 this.netmask = DEFAULT_MASK;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070046 }
47
48 /**
49 * Converts a byte array into an IP address.
50 *
51 * @param address a byte array
52 * @return an IP address
53 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070054 public static IpPrefix valueOf(byte [] address) {
55 return new IpPrefix(Version.INET, address);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070056 }
57
58 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070059 * Converts a byte array into an IP address.
60 *
61 * @param address a byte array
62 * @param netmask the CIDR value subnet mask
63 * @return an IP address
64 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070065 public static IpPrefix valueOf(byte [] address, int netmask) {
66 return new IpPrefix(Version.INET, address, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070067 }
68
69 /**
70 * Helper to convert an integer into a byte array.
71 *
72 * @param address the integer to convert
73 * @return a byte array
74 */
75 private static byte [] bytes(int address) {
76 byte [] bytes = new byte [INET_LEN];
77 for (int i = 0; i < INET_LEN; i++) {
78 bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff);
79 }
80
81 return bytes;
82 }
83
84 /**
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070085 * Converts an integer into an IPv4 address.
86 *
87 * @param address an integer representing an IP value
88 * @return an IP address
89 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070090 public static IpPrefix valueOf(int address) {
91 return new IpPrefix(Version.INET, bytes(address));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070092 }
93
94 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070095 * Converts an integer into an IPv4 address.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070096 *
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070097 * @param address an integer representing an IP value
98 * @param netmask the CIDR value subnet mask
99 * @return an IP address
100 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700101 public static IpPrefix valueOf(int address, int netmask) {
102 return new IpPrefix(Version.INET, bytes(address), netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700103 }
104
105 /**
106 * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -0700107 * string can also be in CIDR (slash) notation. If the netmask is omitted,
108 * it will be set to DEFAULT_MASK (0).
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700109 *
110 * @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 -0700111 * @return an IP address
112 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700113 public static IpPrefix valueOf(String address) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700114
115 final String [] parts = address.split("\\/");
116 if (parts.length > 2) {
117 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700118 + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700119 }
120
121 int mask = DEFAULT_MASK;
122 if (parts.length == 2) {
Yuta HIGUCHI7f3df232014-10-15 23:38:24 -0700123 mask = Integer.parseInt(parts[1]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700124 if (mask > MAX_INET_MASK) {
125 throw new IllegalArgumentException(
126 "Value of subnet mask cannot exceed "
alshabib8f1cf4a2014-09-17 14:44:48 -0700127 + MAX_INET_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700128 }
129 }
130
131 final String [] net = parts[0].split("\\.");
132 if (net.length != INET_LEN) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700133 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700134 + "Address must have four decimal values separated by dots (.)");
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700135 }
136 final byte [] bytes = new byte[INET_LEN];
137 for (int i = 0; i < INET_LEN; i++) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700138 bytes[i] = (byte) Short.parseShort(net[i], 10);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700139 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700140 return new IpPrefix(Version.INET, bytes, mask);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700141 }
142
143 /**
144 * Returns the IP version of this address.
145 *
146 * @return the version
147 */
148 public Version version() {
149 return this.version;
150 }
151
152 /**
153 * Returns the IP address as a byte array.
154 *
155 * @return a byte array
156 */
Yuta HIGUCHI10681f62014-09-21 17:49:46 -0700157 public byte[] toOctets() {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700158 return Arrays.copyOf(this.octets, INET_LEN);
159 }
160
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700161 /**
Yuta HIGUCHI10681f62014-09-21 17:49:46 -0700162 * Returns the IP address prefix length.
163 *
164 * @return prefix length
165 */
166 public int prefixLength() {
167 return netmask;
168 }
169
170 /**
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700171 * Returns the integral value of this IP address.
172 *
173 * @return the IP address's value as an integer
174 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700175 public int toInt() {
alshabib6eb438a2014-10-01 16:39:37 -0700176 int val = 0;
177 for (int i = 0; i < octets.length; i++) {
178 val <<= 8;
179 val |= octets[i] & 0xff;
180 }
181 return val;
182 }
183
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700184 /**
185 * Helper for computing the mask value from CIDR.
186 *
187 * @return an integer bitmask
188 */
189 private int mask() {
190 int shift = MAX_INET_MASK - this.netmask;
191 return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
192 }
193
194 /**
195 * Returns the subnet mask in IpAddress form. The netmask value for
196 * the returned IpAddress is 0, as the address itself is a mask.
197 *
198 * @return the subnet mask
199 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700200 public IpPrefix netmask() {
201 return new IpPrefix(Version.INET, bytes(mask()));
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700202 }
203
204 /**
205 * Returns the network portion of this address as an IpAddress.
206 * The netmask of the returned IpAddress is the current mask. If this
207 * address doesn't have a mask, this returns an all-0 IpAddress.
208 *
209 * @return the network address or null
210 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700211 public IpPrefix network() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700212 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700213 return new IpPrefix(version, ANY, DEFAULT_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700214 }
215
216 byte [] net = new byte [4];
217 byte [] mask = bytes(mask());
218 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700219 net[i] = (byte) (octets[i] & mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700220 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700221 return new IpPrefix(version, net, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700222 }
223
224 /**
225 * Returns the host portion of the IPAddress, as an IPAddress.
226 * The netmask of the returned IpAddress is the current mask. If this
227 * address doesn't have a mask, this returns a copy of the current
228 * address.
229 *
230 * @return the host address
231 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700232 public IpPrefix host() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700233 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700234 new IpPrefix(version, octets, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700235 }
236
237 byte [] host = new byte [INET_LEN];
238 byte [] mask = bytes(mask());
239 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700240 host[i] = (byte) (octets[i] & ~mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700241 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700242 return new IpPrefix(version, host, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700243 }
244
Jonathan Hart70da5122014-10-01 16:37:42 -0700245 /**
246 * Returns an IpAddress of the bytes contained in this prefix.
247 * FIXME this is a hack for now and only works because IpPrefix doesn't
248 * mask the input bytes on creation.
249 *
250 * @return the IpAddress
251 */
252 public IpAddress toIpAddress() {
253 return IpAddress.valueOf(octets);
254 }
255
alshabib8f1cf4a2014-09-17 14:44:48 -0700256 public boolean isMasked() {
257 return mask() != 0;
258 }
259
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700260 /**
261 * Determines whether a given address is contained within this IpAddress'
262 * network.
263 *
264 * @param other another IP address that could be contained in this network
265 * @return true if the other IP address is contained in this address'
266 * network, otherwise false
267 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700268 public boolean contains(IpPrefix other) {
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700269 if (this.netmask <= other.netmask) {
270 // Special case where they're both /32 addresses
271 if (this.netmask == MAX_INET_MASK) {
272 return Arrays.equals(octets, other.octets);
273 }
274
275 // Mask the other address with our network mask
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700276 IpPrefix otherMasked =
277 IpPrefix.valueOf(other.octets, netmask).network();
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700278
279 return network().equals(otherMasked);
280 }
281 return false;
282 }
283
Jonathan Hart70da5122014-10-01 16:37:42 -0700284 public boolean contains(IpAddress address) {
285 // Need to get the network address because prefixes aren't automatically
286 // masked on creation
287 IpPrefix meMasked = network();
288
289 IpPrefix otherMasked =
290 IpPrefix.valueOf(address.octets, netmask).network();
291
292 return Arrays.equals(meMasked.octets, otherMasked.octets);
293 }
294
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700295 @Override
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700296 public int hashCode() {
297 final int prime = 31;
298 int result = 1;
299 result = prime * result + netmask;
300 result = prime * result + Arrays.hashCode(octets);
301 result = prime * result + ((version == null) ? 0 : version.hashCode());
302 return result;
303 }
304
305 @Override
306 public boolean equals(Object obj) {
307 if (this == obj) {
308 return true;
309 }
310 if (obj == null) {
311 return false;
312 }
313 if (getClass() != obj.getClass()) {
314 return false;
315 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700316 IpPrefix other = (IpPrefix) obj;
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700317 if (netmask != other.netmask) {
318 return false;
319 }
Jonathan Hart70da5122014-10-01 16:37:42 -0700320 // TODO not quite right until we mask the input
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700321 if (!Arrays.equals(octets, other.octets)) {
322 return false;
323 }
324 if (version != other.version) {
325 return false;
326 }
327 return true;
328 }
329
330 @Override
331 /*
332 * (non-Javadoc)
333 * format is "x.x.x.x" for non-masked (netmask 0) addresses,
334 * and "x.x.x.x/y" for masked addresses.
335 *
336 * @see java.lang.Object#toString()
337 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700338 public String toString() {
339 final StringBuilder builder = new StringBuilder();
340 for (final byte b : this.octets) {
341 if (builder.length() > 0) {
342 builder.append(".");
343 }
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700344 builder.append(String.format("%d", b & 0xff));
345 }
346 if (netmask != DEFAULT_MASK) {
347 builder.append("/");
348 builder.append(String.format("%d", netmask));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700349 }
350 return builder.toString();
351 }
352
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700353}