blob: c73bf9ad41102950d2f7a318e210917f90783a26 [file] [log] [blame]
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -07001package org.onlab.packet;
2
3import java.util.Arrays;
4
5/**
6 * A class representing an IPv4 address.
7 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -07008public final class IpPrefix {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -07009
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -070010 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
11
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070012 //IP Versions
13 public enum Version { INET, INET6 };
14
Ayaka Koshibe04a1a4e2014-09-11 14:31:29 -070015 //lengths of address, in bytes
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070016 public static final int INET_LEN = 4;
Ayaka Koshibe3a25aec2014-09-12 11:52:53 -070017 public static final int INET6_LEN = 16;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070018
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070019 //maximum CIDR value
20 public static final int MAX_INET_MASK = 32;
Ayaka Koshibeb55524f2014-09-18 09:59:24 -070021 //no mask (no network), e.g. a simple address
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070022 public static final int DEFAULT_MASK = 0;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070023
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070024 /**
25 * Default value indicating an unspecified address.
26 */
27 public static final byte [] ANY = new byte [] {0, 0, 0, 0};
28
29 protected Version version;
30
31 protected byte[] octets;
32 protected int netmask;
33
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070034 private IpPrefix(Version ver, byte[] octets, int netmask) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070035 this.version = ver;
36 this.octets = Arrays.copyOf(octets, INET_LEN);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070037 this.netmask = netmask;
38 }
39
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070040 private IpPrefix(Version ver, byte[] octets) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070041 this.version = ver;
42 this.octets = Arrays.copyOf(octets, INET_LEN);
43 this.netmask = DEFAULT_MASK;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070044 }
45
46 /**
47 * Converts a byte array into an IP address.
48 *
49 * @param address a byte array
50 * @return an IP address
51 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070052 public static IpPrefix valueOf(byte [] address) {
53 return new IpPrefix(Version.INET, address);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070054 }
55
56 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070057 * Converts a byte array into an IP address.
58 *
59 * @param address a byte array
60 * @param netmask the CIDR value subnet mask
61 * @return an IP address
62 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070063 public static IpPrefix valueOf(byte [] address, int netmask) {
64 return new IpPrefix(Version.INET, address, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070065 }
66
67 /**
68 * Helper to convert an integer into a byte array.
69 *
70 * @param address the integer to convert
71 * @return a byte array
72 */
73 private static byte [] bytes(int address) {
74 byte [] bytes = new byte [INET_LEN];
75 for (int i = 0; i < INET_LEN; i++) {
76 bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff);
77 }
78
79 return bytes;
80 }
81
82 /**
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070083 * Converts an integer into an IPv4 address.
84 *
85 * @param address an integer representing an IP value
86 * @return an IP address
87 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070088 public static IpPrefix valueOf(int address) {
89 return new IpPrefix(Version.INET, bytes(address));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070090 }
91
92 /**
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070093 * Converts an integer into an IPv4 address.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070094 *
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070095 * @param address an integer representing an IP value
96 * @param netmask the CIDR value subnet mask
97 * @return an IP address
98 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070099 public static IpPrefix valueOf(int address, int netmask) {
100 return new IpPrefix(Version.INET, bytes(address), netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700101 }
102
103 /**
104 * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
Ayaka Koshibe08eabaa2014-09-17 14:59:25 -0700105 * string can also be in CIDR (slash) notation. If the netmask is omitted,
106 * it will be set to DEFAULT_MASK (0).
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700107 *
108 * @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 -0700109 * @return an IP address
110 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700111 public static IpPrefix valueOf(String address) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700112
113 final String [] parts = address.split("\\/");
114 if (parts.length > 2) {
115 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700116 + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700117 }
118
119 int mask = DEFAULT_MASK;
120 if (parts.length == 2) {
121 mask = Integer.valueOf(parts[1]);
122 if (mask > MAX_INET_MASK) {
123 throw new IllegalArgumentException(
124 "Value of subnet mask cannot exceed "
alshabib8f1cf4a2014-09-17 14:44:48 -0700125 + MAX_INET_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700126 }
127 }
128
129 final String [] net = parts[0].split("\\.");
130 if (net.length != INET_LEN) {
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700131 throw new IllegalArgumentException("Malformed IP address string; "
Ayaka Koshibeb55524f2014-09-18 09:59:24 -0700132 + "Address must have four decimal values separated by dots (.)");
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700133 }
134 final byte [] bytes = new byte[INET_LEN];
135 for (int i = 0; i < INET_LEN; i++) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700136 bytes[i] = (byte) Short.parseShort(net[i], 10);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700137 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700138 return new IpPrefix(Version.INET, bytes, mask);
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700139 }
140
141 /**
142 * Returns the IP version of this address.
143 *
144 * @return the version
145 */
146 public Version version() {
147 return this.version;
148 }
149
150 /**
151 * Returns the IP address as a byte array.
152 *
153 * @return a byte array
154 */
155 public byte [] toOctets() {
156 return Arrays.copyOf(this.octets, INET_LEN);
157 }
158
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700159 /**
160 * Returns the integral value of this IP address.
161 *
162 * @return the IP address's value as an integer
163 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700164 public int toInt() {
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700165 int address = 0;
166 for (int i = 0; i < INET_LEN; i++) {
167 address |= octets[i] << ((INET_LEN - (i + 1)) * 8);
168 }
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700169 return address;
170 }
171
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700172 /**
173 * Helper for computing the mask value from CIDR.
174 *
175 * @return an integer bitmask
176 */
177 private int mask() {
178 int shift = MAX_INET_MASK - this.netmask;
179 return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
180 }
181
182 /**
183 * Returns the subnet mask in IpAddress form. The netmask value for
184 * the returned IpAddress is 0, as the address itself is a mask.
185 *
186 * @return the subnet mask
187 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700188 public IpPrefix netmask() {
189 return new IpPrefix(Version.INET, bytes(mask()));
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700190 }
191
192 /**
193 * Returns the network portion of this address as an IpAddress.
194 * The netmask of the returned IpAddress is the current mask. If this
195 * address doesn't have a mask, this returns an all-0 IpAddress.
196 *
197 * @return the network address or null
198 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700199 public IpPrefix network() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700200 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700201 return new IpPrefix(version, ANY, DEFAULT_MASK);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700202 }
203
204 byte [] net = new byte [4];
205 byte [] mask = bytes(mask());
206 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700207 net[i] = (byte) (octets[i] & mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700208 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700209 return new IpPrefix(version, net, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700210 }
211
212 /**
213 * Returns the host portion of the IPAddress, as an IPAddress.
214 * The netmask of the returned IpAddress is the current mask. If this
215 * address doesn't have a mask, this returns a copy of the current
216 * address.
217 *
218 * @return the host address
219 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700220 public IpPrefix host() {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700221 if (netmask == DEFAULT_MASK) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700222 new IpPrefix(version, octets, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700223 }
224
225 byte [] host = new byte [INET_LEN];
226 byte [] mask = bytes(mask());
227 for (int i = 0; i < INET_LEN; i++) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700228 host[i] = (byte) (octets[i] & ~mask[i]);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700229 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700230 return new IpPrefix(version, host, netmask);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700231 }
232
alshabib8f1cf4a2014-09-17 14:44:48 -0700233 public boolean isMasked() {
234 return mask() != 0;
235 }
236
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700237 /**
238 * Determines whether a given address is contained within this IpAddress'
239 * network.
240 *
241 * @param other another IP address that could be contained in this network
242 * @return true if the other IP address is contained in this address'
243 * network, otherwise false
244 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700245 public boolean contains(IpPrefix other) {
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700246 if (this.netmask <= other.netmask) {
247 // Special case where they're both /32 addresses
248 if (this.netmask == MAX_INET_MASK) {
249 return Arrays.equals(octets, other.octets);
250 }
251
252 // Mask the other address with our network mask
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700253 IpPrefix otherMasked =
254 IpPrefix.valueOf(other.octets, netmask).network();
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700255
256 return network().equals(otherMasked);
257 }
258 return false;
259 }
260
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700261 @Override
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700262 public int hashCode() {
263 final int prime = 31;
264 int result = 1;
265 result = prime * result + netmask;
266 result = prime * result + Arrays.hashCode(octets);
267 result = prime * result + ((version == null) ? 0 : version.hashCode());
268 return result;
269 }
270
271 @Override
272 public boolean equals(Object obj) {
273 if (this == obj) {
274 return true;
275 }
276 if (obj == null) {
277 return false;
278 }
279 if (getClass() != obj.getClass()) {
280 return false;
281 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700282 IpPrefix other = (IpPrefix) obj;
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700283 if (netmask != other.netmask) {
284 return false;
285 }
286 if (!Arrays.equals(octets, other.octets)) {
287 return false;
288 }
289 if (version != other.version) {
290 return false;
291 }
292 return true;
293 }
294
295 @Override
296 /*
297 * (non-Javadoc)
298 * format is "x.x.x.x" for non-masked (netmask 0) addresses,
299 * and "x.x.x.x/y" for masked addresses.
300 *
301 * @see java.lang.Object#toString()
302 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700303 public String toString() {
304 final StringBuilder builder = new StringBuilder();
305 for (final byte b : this.octets) {
306 if (builder.length() > 0) {
307 builder.append(".");
308 }
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700309 builder.append(String.format("%d", b & 0xff));
310 }
311 if (netmask != DEFAULT_MASK) {
312 builder.append("/");
313 builder.append(String.format("%d", netmask));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700314 }
315 return builder.toString();
316 }
317
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700318}