blob: a440b984f59a8217dcf818149c428ec25ddf883d [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() {
Jonathan Hartdc711bd2014-10-15 11:24:23 -0700179 int val = 0;
180 for (int i = 0; i < octets.length; i++) {
181 val <<= 8;
182 val |= octets[i] & 0xff;
183 }
184 return val;
185 }
186
Jonathan Hartdec62d42014-09-22 15:59:04 -0700187 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700188 * Converts the IP address to a /32 IP prefix.
189 *
190 * @return the new IP prefix
191 */
192 public IpPrefix toPrefix() {
193 return IpPrefix.valueOf(octets, MAX_INET_MASK);
194 }
195
196 /**
Jonathan Hartdec62d42014-09-22 15:59:04 -0700197 * Helper for computing the mask value from CIDR.
198 *
199 * @return an integer bitmask
200 */
201 private int mask() {
202 int shift = MAX_INET_MASK - this.netmask;
203 return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
204 }
205
206 /**
207 * Returns the subnet mask in IpAddress form. The netmask value for
208 * the returned IpAddress is 0, as the address itself is a mask.
209 *
210 * @return the subnet mask
211 */
212 public IpAddress netmask() {
213 return new IpAddress(Version.INET, bytes(mask()));
214 }
215
216 /**
217 * Returns the network portion of this address as an IpAddress.
218 * The netmask of the returned IpAddress is the current mask. If this
219 * address doesn't have a mask, this returns an all-0 IpAddress.
220 *
221 * @return the network address or null
222 */
223 public IpAddress network() {
224 if (netmask == DEFAULT_MASK) {
225 return new IpAddress(version, ANY, DEFAULT_MASK);
226 }
227
228 byte [] net = new byte [4];
229 byte [] mask = bytes(mask());
230 for (int i = 0; i < INET_LEN; i++) {
231 net[i] = (byte) (octets[i] & mask[i]);
232 }
233 return new IpAddress(version, net, netmask);
234 }
235
236 /**
237 * Returns the host portion of the IPAddress, as an IPAddress.
238 * The netmask of the returned IpAddress is the current mask. If this
239 * address doesn't have a mask, this returns a copy of the current
240 * address.
241 *
242 * @return the host address
243 */
244 public IpAddress host() {
245 if (netmask == DEFAULT_MASK) {
246 new IpAddress(version, octets, netmask);
247 }
248
249 byte [] host = new byte [INET_LEN];
250 byte [] mask = bytes(mask());
251 for (int i = 0; i < INET_LEN; i++) {
252 host[i] = (byte) (octets[i] & ~mask[i]);
253 }
254 return new IpAddress(version, host, netmask);
255 }
256
257 public boolean isMasked() {
258 return mask() != 0;
259 }
260
261 /**
262 * Determines whether a given address is contained within this IpAddress'
263 * network.
264 *
265 * @param other another IP address that could be contained in this network
266 * @return true if the other IP address is contained in this address'
267 * network, otherwise false
268 */
269 public boolean contains(IpAddress other) {
270 if (this.netmask <= other.netmask) {
271 // Special case where they're both /32 addresses
272 if (this.netmask == MAX_INET_MASK) {
273 return Arrays.equals(octets, other.octets);
274 }
275
276 // Mask the other address with our network mask
277 IpAddress otherMasked =
278 IpAddress.valueOf(other.octets, netmask).network();
279
280 return network().equals(otherMasked);
281 }
282 return false;
283 }
284
285 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700286 public int compareTo(IpAddress o) {
Jonathan Hartbcae7bd2014-10-16 10:24:41 -0700287 Long lv = ((long) this.toInt()) & 0xffffffffL;
288 Long rv = ((long) o.toInt()) & 0xffffffffL;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700289 return lv.compareTo(rv);
290 }
291
292 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700293 public int hashCode() {
294 final int prime = 31;
295 int result = 1;
296 result = prime * result + netmask;
297 result = prime * result + Arrays.hashCode(octets);
298 result = prime * result + ((version == null) ? 0 : version.hashCode());
299 return result;
300 }
301
302 @Override
303 public boolean equals(Object obj) {
304 if (this == obj) {
305 return true;
306 }
307 if (obj == null) {
308 return false;
309 }
310 if (getClass() != obj.getClass()) {
311 return false;
312 }
313 IpAddress other = (IpAddress) obj;
314 if (netmask != other.netmask) {
315 return false;
316 }
317 if (!Arrays.equals(octets, other.octets)) {
318 return false;
319 }
320 if (version != other.version) {
321 return false;
322 }
323 return true;
324 }
325
326 @Override
327 /*
328 * (non-Javadoc)
329 * format is "x.x.x.x" for non-masked (netmask 0) addresses,
330 * and "x.x.x.x/y" for masked addresses.
331 *
332 * @see java.lang.Object#toString()
333 */
334 public String toString() {
335 final StringBuilder builder = new StringBuilder();
336 for (final byte b : this.octets) {
337 if (builder.length() > 0) {
338 builder.append(".");
339 }
340 builder.append(String.format("%d", b & 0xff));
341 }
342 if (netmask != DEFAULT_MASK) {
343 builder.append("/");
344 builder.append(String.format("%d", netmask));
345 }
346 return builder.toString();
347 }
348
349}