blob: 8ad911293ac1e846bbffdf9c27a458d473b35167 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
Pavlin Radoslavov9de27722014-10-23 20:31:15 -070016package org.onlab.packet;
17
18import java.net.InetAddress;
19import java.net.UnknownHostException;
20import java.nio.ByteBuffer;
21import java.util.Objects;
22
23import com.google.common.net.InetAddresses;
24import com.google.common.primitives.UnsignedLongs;
25
26import static com.google.common.base.Preconditions.checkNotNull;
27import static com.google.common.base.Preconditions.checkState;
28
29/**
30 * The class representing an IPv6 address.
31 * This class is immutable.
32 */
33public final class Ip6Address implements Comparable<Ip6Address> {
34 private final long valueHigh; // The higher (more significant) 64 bits
35 private final long valueLow; // The lower (less significant) 64 bits
36
37 /** The length of the address in bytes (octets). */
38 public static final int BYTE_LENGTH = 16;
39
40 /** The length of the address in bits. */
41 public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
42
43 /**
44 * Default constructor.
45 */
46 public Ip6Address() {
47 this.valueHigh = 0;
48 this.valueLow = 0;
49 }
50
51 /**
52 * Copy constructor.
53 *
54 * @param other the object to copy from
55 */
56 public Ip6Address(Ip6Address other) {
57 this.valueHigh = other.valueHigh;
58 this.valueLow = other.valueLow;
59 }
60
61 /**
62 * Constructor from integer values.
63 *
64 * @param valueHigh the higher (more significant) 64 bits of the address
65 * @param valueLow the lower (less significant) 64 bits of the address
66 */
67 public Ip6Address(long valueHigh, long valueLow) {
68 this.valueHigh = valueHigh;
69 this.valueLow = valueLow;
70 }
71
72 /**
73 * Constructor from a byte array with the IPv6 address stored in network
74 * byte order (i.e., the most significant byte first).
75 *
76 * @param value the value to use
77 */
78 public Ip6Address(byte[] value) {
79 this(value, 0);
80 }
81
82 /**
83 * Constructor from a byte array with the IPv6 address stored in network
84 * byte order (i.e., the most significant byte first), and a given offset
85 * from the beginning of the byte array.
86 *
87 * @param value the value to use
88 * @param offset the offset in bytes from the beginning of the byte array
89 */
90 public Ip6Address(byte[] value, int offset) {
91 checkNotNull(value);
92
93 // Verify the arguments
94 if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
95 String msg;
96 if (value.length < BYTE_LENGTH) {
97 msg = "Invalid IPv6 address array: array length: " +
98 value.length + ". Must be at least " + BYTE_LENGTH;
99 } else {
100 msg = "Invalid IPv6 address array: array offset: " +
101 offset + ". Must be in the interval [0, " +
102 (value.length - BYTE_LENGTH) + "]";
103 }
104 throw new IllegalArgumentException(msg);
105 }
106
107 // Read the address
108 ByteBuffer bb = ByteBuffer.wrap(value);
109 bb.position(offset);
110 this.valueHigh = bb.getLong();
111 this.valueLow = bb.getLong();
112 }
113
114 /**
115 * Constructs an IPv6 address from a string representation of the address.
116 *<p>
117 * Example: "1111:2222::8888"
118 *
119 * @param value the value to use
120 */
121 public Ip6Address(String value) {
122 checkNotNull(value);
123
124 if (value.isEmpty()) {
125 final String msg = "Specified IPv6 cannot be an empty string";
126 throw new IllegalArgumentException(msg);
127 }
128 InetAddress addr = null;
129 try {
130 addr = InetAddresses.forString(value);
131 } catch (IllegalArgumentException e) {
132 final String msg = "Invalid IPv6 address string: " + value;
133 throw new IllegalArgumentException(msg);
134 }
135 byte[] bytes = addr.getAddress();
136 ByteBuffer bb = ByteBuffer.wrap(bytes);
137 this.valueHigh = bb.getLong();
138 this.valueLow = bb.getLong();
139 }
140
141 /**
142 * Gets the IPv6 address as a byte array.
143 *
144 * @return a byte array with the IPv6 address stored in network byte order
145 * (i.e., the most significant byte first).
146 */
147 public byte[] toOctets() {
148 return ByteBuffer.allocate(BYTE_LENGTH)
149 .putLong(valueHigh).putLong(valueLow).array();
150 }
151
152 /**
153 * Creates an IPv6 network mask prefix.
154 *
155 * @param prefixLen the length of the mask prefix. Must be in the interval
156 * [0, 128].
157 * @return a new IPv6 address that contains a mask prefix of the
158 * specified length
159 */
160 public static Ip6Address makeMaskPrefix(int prefixLen) {
161 long vh, vl;
162
163 // Verify the prefix length
164 if ((prefixLen < 0) || (prefixLen > Ip6Address.BIT_LENGTH)) {
165 final String msg = "Invalid IPv6 prefix length: " + prefixLen +
166 ". Must be in the interval [0, 128].";
167 throw new IllegalArgumentException(msg);
168 }
169
170 if (prefixLen == 0) {
171 //
172 // NOTE: Apparently, the result of "<< 64" shifting to the left
173 // results in all 1s instead of all 0s, hence we handle it as
174 // a special case.
175 //
176 vh = 0;
177 vl = 0;
178 } else if (prefixLen <= 64) {
179 vh = (0xffffffffffffffffL << (64 - prefixLen)) & 0xffffffffffffffffL;
180 vl = 0;
181 } else {
182 vh = -1L; // All 1s
183 vl = (0xffffffffffffffffL << (128 - prefixLen)) & 0xffffffffffffffffL;
184 }
185 return new Ip6Address(vh, vl);
186 }
187
188 /**
189 * Creates an IPv6 address by masking it with a network mask of given
190 * mask length.
191 *
192 * @param addr the address to mask
193 * @param prefixLen the length of the mask prefix. Must be in the interval
194 * [0, 128].
195 * @return a new IPv6 address that is masked with a mask prefix of the
196 * specified length
197 */
198 public static Ip6Address makeMaskedAddress(final Ip6Address addr,
199 int prefixLen) {
200 Ip6Address mask = Ip6Address.makeMaskPrefix(prefixLen);
201 long vh = addr.valueHigh & mask.valueHigh;
202 long vl = addr.valueLow & mask.valueLow;
203
204 return new Ip6Address(vh, vl);
205 }
206
207 /**
208 * Gets the value of the higher (more significant) 64 bits of the address.
209 *
210 * @return the value of the higher (more significant) 64 bits of the
211 * address
212 */
213 public long getValueHigh() {
214 return valueHigh;
215 }
216
217 /**
218 * Gets the value of the lower (less significant) 64 bits of the address.
219 *
220 * @return the value of the lower (less significant) 64 bits of the
221 * address
222 */
223 public long getValueLow() {
224 return valueLow;
225 }
226
227 /**
228 * Converts the IPv6 value to a ':' separated string.
229 *
230 * @return the IPv6 value as a ':' separated string
231 */
232 @Override
233 public String toString() {
234 ByteBuffer bb = ByteBuffer.allocate(Ip6Address.BYTE_LENGTH);
235 bb.putLong(valueHigh);
236 bb.putLong(valueLow);
237 InetAddress inetAddr = null;
238 try {
239 inetAddr = InetAddress.getByAddress(bb.array());
240 } catch (UnknownHostException e) {
241 // Should never happen
242 checkState(false, "Internal error: Ip6Address.toString()");
243 return "::";
244 }
245 return InetAddresses.toAddrString(inetAddr);
246 }
247
248 @Override
249 public boolean equals(Object o) {
250 if (!(o instanceof Ip6Address)) {
251 return false;
252 }
253 Ip6Address other = (Ip6Address) o;
254 return this.valueHigh == other.valueHigh
255 && this.valueLow == other.valueLow;
256 }
257
258 @Override
259 public int hashCode() {
260 return Objects.hash(valueHigh, valueLow);
261 }
262
263 @Override
264 public int compareTo(Ip6Address o) {
265 // Compare the high-order 64-bit value
266 if (this.valueHigh != o.valueHigh) {
267 return UnsignedLongs.compare(this.valueHigh, o.valueHigh);
268 }
269 // Compare the low-order 64-bit value
270 if (this.valueLow != o.valueLow) {
271 return UnsignedLongs.compare(this.valueLow, o.valueLow);
272 }
273 return 0;
274 }
275}