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