blob: 60a592e6fd52fa54e96f1e8cf592dc11451cd05e [file] [log] [blame]
Jonathan Hartdec62d42014-09-22 15:59:04 -07001package org.onlab.packet;
2
3import java.util.Arrays;
4
Jonathan Hartab63aac2014-10-16 08:52:55 -07005
6
Jonathan Hartdec62d42014-09-22 15:59:04 -07007/**
8 * A class representing an IPv4 address.
9 * <p/>
10 * TODO this class is a clone of IpPrefix and still needs to be modified to
11 * look more like an IpAddress.
12 */
Jonathan Hartab63aac2014-10-16 08:52:55 -070013public final class IpAddress implements Comparable<IpAddress> {
Jonathan Hartdec62d42014-09-22 15:59:04 -070014
15 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
16
17 //IP Versions
18 public enum Version { INET, INET6 };
19
20 //lengths of address, in bytes
21 public static final int INET_LEN = 4;
22 public static final int INET6_LEN = 16;
23
24 //maximum CIDR value
25 public static final int MAX_INET_MASK = 32;
26 //no mask (no network), e.g. a simple address
27 public static final int DEFAULT_MASK = 0;
28
29 /**
30 * Default value indicating an unspecified address.
31 */
32 static final byte[] ANY = new byte [] {0, 0, 0, 0};
33
34 protected Version version;
35
36 protected byte[] octets;
37 protected int netmask;
38
39 private IpAddress(Version ver, byte[] octets, int netmask) {
40 this.version = ver;
41 this.octets = Arrays.copyOf(octets, INET_LEN);
42 this.netmask = netmask;
43 }
44
45 private IpAddress(Version ver, byte[] octets) {
46 this.version = ver;
47 this.octets = Arrays.copyOf(octets, INET_LEN);
48 this.netmask = DEFAULT_MASK;
49 }
50
51 /**
52 * Converts a byte array into an IP address.
53 *
54 * @param address a byte array
55 * @return an IP address
56 */
57 public static IpAddress valueOf(byte [] address) {
58 return new IpAddress(Version.INET, address);
59 }
60
61 /**
62 * Converts a byte array into an IP address.
63 *
64 * @param address a byte array
65 * @param netmask the CIDR value subnet mask
66 * @return an IP address
67 */
68 public static IpAddress valueOf(byte [] address, int netmask) {
69 return new IpAddress(Version.INET, address, netmask);
70 }
71
72 /**
73 * Helper to convert an integer into a byte array.
74 *
75 * @param address the integer to convert
76 * @return a byte array
77 */
78 private static byte [] bytes(int address) {
79 byte [] bytes = new byte [INET_LEN];
80 for (int i = 0; i < INET_LEN; i++) {
81 bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff);
82 }
83
84 return bytes;
85 }
86
87 /**
88 * Converts an integer into an IPv4 address.
89 *
90 * @param address an integer representing an IP value
91 * @return an IP address
92 */
93 public static IpAddress valueOf(int address) {
94 return new IpAddress(Version.INET, bytes(address));
95 }
96
97 /**
98 * Converts an integer into an IPv4 address.
99 *
100 * @param address an integer representing an IP value
101 * @param netmask the CIDR value subnet mask
102 * @return an IP address
103 */
104 public static IpAddress valueOf(int address, int netmask) {
105 return new IpAddress(Version.INET, bytes(address), netmask);
106 }
107
108 /**
109 * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
110 * string can also be in CIDR (slash) notation. If the netmask is omitted,
111 * it will be set to DEFAULT_MASK (0).
112 *
113 * @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24"
114 * @return an IP address
115 */
116 public static IpAddress valueOf(String address) {
117
118 final String [] parts = address.split("\\/");
119 if (parts.length > 2) {
120 throw new IllegalArgumentException("Malformed IP address string; "
121 + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
122 }
123
124 int mask = DEFAULT_MASK;
125 if (parts.length == 2) {
Yuta HIGUCHI7f3df232014-10-15 23:38:24 -0700126 mask = Integer.parseInt(parts[1]);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700127 if (mask > MAX_INET_MASK) {
128 throw new IllegalArgumentException(
129 "Value of subnet mask cannot exceed "
130 + MAX_INET_MASK);
131 }
132 }
133
134 final String [] net = parts[0].split("\\.");
135 if (net.length != INET_LEN) {
136 throw new IllegalArgumentException("Malformed IP address string; "
137 + "Address must have four decimal values separated by dots (.)");
138 }
139 final byte [] bytes = new byte[INET_LEN];
140 for (int i = 0; i < INET_LEN; i++) {
141 bytes[i] = (byte) Short.parseShort(net[i], 10);
142 }
143 return new IpAddress(Version.INET, bytes, mask);
144 }
145
146 /**
147 * Returns the IP version of this address.
148 *
149 * @return the version
150 */
151 public Version version() {
152 return this.version;
153 }
154
155 /**
156 * Returns the IP address as a byte array.
157 *
158 * @return a byte array
159 */
160 public byte[] toOctets() {
161 return Arrays.copyOf(this.octets, INET_LEN);
162 }
163
164 /**
165 * Returns the IP address prefix length.
166 *
167 * @return prefix length
168 */
169 public int prefixLength() {
170 return netmask;
171 }
172
173 /**
174 * Returns the integral value of this IP address.
175 *
176 * @return the IP address's value as an integer
177 */
178 public int toInt() {
179 int address = 0;
180 for (int i = 0; i < INET_LEN; i++) {
181 address |= octets[i] << ((INET_LEN - (i + 1)) * 8);
182 }
183 return address;
184 }
185
Jonathan Hartdc711bd2014-10-15 11:24:23 -0700186 public int toRealInt() {
187 int val = 0;
188 for (int i = 0; i < octets.length; i++) {
189 val <<= 8;
190 val |= octets[i] & 0xff;
191 }
192 return val;
193 }
194
Jonathan Hartdec62d42014-09-22 15:59:04 -0700195 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700196 * Converts the IP address to a /32 IP prefix.
197 *
198 * @return the new IP prefix
199 */
200 public IpPrefix toPrefix() {
201 return IpPrefix.valueOf(octets, MAX_INET_MASK);
202 }
203
204 /**
Jonathan Hartdec62d42014-09-22 15:59:04 -0700205 * Helper for computing the mask value from CIDR.
206 *
207 * @return an integer bitmask
208 */
209 private int mask() {
210 int shift = MAX_INET_MASK - this.netmask;
211 return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
212 }
213
214 /**
215 * Returns the subnet mask in IpAddress form. The netmask value for
216 * the returned IpAddress is 0, as the address itself is a mask.
217 *
218 * @return the subnet mask
219 */
220 public IpAddress netmask() {
221 return new IpAddress(Version.INET, bytes(mask()));
222 }
223
224 /**
225 * Returns the network portion of this address 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 an all-0 IpAddress.
228 *
229 * @return the network address or null
230 */
231 public IpAddress network() {
232 if (netmask == DEFAULT_MASK) {
233 return new IpAddress(version, ANY, DEFAULT_MASK);
234 }
235
236 byte [] net = new byte [4];
237 byte [] mask = bytes(mask());
238 for (int i = 0; i < INET_LEN; i++) {
239 net[i] = (byte) (octets[i] & mask[i]);
240 }
241 return new IpAddress(version, net, netmask);
242 }
243
244 /**
245 * Returns the host portion of the IPAddress, as an IPAddress.
246 * The netmask of the returned IpAddress is the current mask. If this
247 * address doesn't have a mask, this returns a copy of the current
248 * address.
249 *
250 * @return the host address
251 */
252 public IpAddress host() {
253 if (netmask == DEFAULT_MASK) {
254 new IpAddress(version, octets, netmask);
255 }
256
257 byte [] host = new byte [INET_LEN];
258 byte [] mask = bytes(mask());
259 for (int i = 0; i < INET_LEN; i++) {
260 host[i] = (byte) (octets[i] & ~mask[i]);
261 }
262 return new IpAddress(version, host, netmask);
263 }
264
265 public boolean isMasked() {
266 return mask() != 0;
267 }
268
269 /**
270 * Determines whether a given address is contained within this IpAddress'
271 * network.
272 *
273 * @param other another IP address that could be contained in this network
274 * @return true if the other IP address is contained in this address'
275 * network, otherwise false
276 */
277 public boolean contains(IpAddress other) {
278 if (this.netmask <= other.netmask) {
279 // Special case where they're both /32 addresses
280 if (this.netmask == MAX_INET_MASK) {
281 return Arrays.equals(octets, other.octets);
282 }
283
284 // Mask the other address with our network mask
285 IpAddress otherMasked =
286 IpAddress.valueOf(other.octets, netmask).network();
287
288 return network().equals(otherMasked);
289 }
290 return false;
291 }
292
293 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700294 public int compareTo(IpAddress o) {
295 Long lv = ((long) this.toRealInt()) & 0xffffffffL;
296 Long rv = ((long) o.toRealInt()) & 0xffffffffL;
297 return lv.compareTo(rv);
298 }
299
300 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700301 public int hashCode() {
302 final int prime = 31;
303 int result = 1;
304 result = prime * result + netmask;
305 result = prime * result + Arrays.hashCode(octets);
306 result = prime * result + ((version == null) ? 0 : version.hashCode());
307 return result;
308 }
309
310 @Override
311 public boolean equals(Object obj) {
312 if (this == obj) {
313 return true;
314 }
315 if (obj == null) {
316 return false;
317 }
318 if (getClass() != obj.getClass()) {
319 return false;
320 }
321 IpAddress other = (IpAddress) obj;
322 if (netmask != other.netmask) {
323 return false;
324 }
325 if (!Arrays.equals(octets, other.octets)) {
326 return false;
327 }
328 if (version != other.version) {
329 return false;
330 }
331 return true;
332 }
333
334 @Override
335 /*
336 * (non-Javadoc)
337 * format is "x.x.x.x" for non-masked (netmask 0) addresses,
338 * and "x.x.x.x/y" for masked addresses.
339 *
340 * @see java.lang.Object#toString()
341 */
342 public String toString() {
343 final StringBuilder builder = new StringBuilder();
344 for (final byte b : this.octets) {
345 if (builder.length() > 0) {
346 builder.append(".");
347 }
348 builder.append(String.format("%d", b & 0xff));
349 }
350 if (netmask != DEFAULT_MASK) {
351 builder.append("/");
352 builder.append(String.format("%d", netmask));
353 }
354 return builder.toString();
355 }
356
357}