| /* |
| * Copyright 2014 Open Networking Laboratory |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onlab.packet; |
| |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.nio.ByteBuffer; |
| import java.util.Objects; |
| |
| import com.google.common.net.InetAddresses; |
| import com.google.common.primitives.UnsignedLongs; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Preconditions.checkState; |
| |
| /** |
| * The class representing an IPv6 address. |
| * This class is immutable. |
| */ |
| public final class Ip6Address implements Comparable<Ip6Address> { |
| private final long valueHigh; // The higher (more significant) 64 bits |
| private final long valueLow; // The lower (less significant) 64 bits |
| |
| /** The length of the address in bytes (octets). */ |
| public static final int BYTE_LENGTH = 16; |
| |
| /** The length of the address in bits. */ |
| public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE; |
| |
| /** |
| * Default constructor. |
| */ |
| public Ip6Address() { |
| this.valueHigh = 0; |
| this.valueLow = 0; |
| } |
| |
| /** |
| * Copy constructor. |
| * |
| * @param other the object to copy from |
| */ |
| public Ip6Address(Ip6Address other) { |
| this.valueHigh = other.valueHigh; |
| this.valueLow = other.valueLow; |
| } |
| |
| /** |
| * Constructor from integer values. |
| * |
| * @param valueHigh the higher (more significant) 64 bits of the address |
| * @param valueLow the lower (less significant) 64 bits of the address |
| */ |
| public Ip6Address(long valueHigh, long valueLow) { |
| this.valueHigh = valueHigh; |
| this.valueLow = valueLow; |
| } |
| |
| /** |
| * Constructor from a byte array with the IPv6 address stored in network |
| * byte order (i.e., the most significant byte first). |
| * |
| * @param value the value to use |
| */ |
| public Ip6Address(byte[] value) { |
| this(value, 0); |
| } |
| |
| /** |
| * Constructor from a byte array with the IPv6 address stored in network |
| * byte order (i.e., the most significant byte first), and a given offset |
| * from the beginning of the byte array. |
| * |
| * @param value the value to use |
| * @param offset the offset in bytes from the beginning of the byte array |
| */ |
| public Ip6Address(byte[] value, int offset) { |
| checkNotNull(value); |
| |
| // Verify the arguments |
| if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) { |
| String msg; |
| if (value.length < BYTE_LENGTH) { |
| msg = "Invalid IPv6 address array: array length: " + |
| value.length + ". Must be at least " + BYTE_LENGTH; |
| } else { |
| msg = "Invalid IPv6 address array: array offset: " + |
| offset + ". Must be in the interval [0, " + |
| (value.length - BYTE_LENGTH) + "]"; |
| } |
| throw new IllegalArgumentException(msg); |
| } |
| |
| // Read the address |
| ByteBuffer bb = ByteBuffer.wrap(value); |
| bb.position(offset); |
| this.valueHigh = bb.getLong(); |
| this.valueLow = bb.getLong(); |
| } |
| |
| /** |
| * Constructs an IPv6 address from a string representation of the address. |
| *<p> |
| * Example: "1111:2222::8888" |
| * |
| * @param value the value to use |
| */ |
| public Ip6Address(String value) { |
| checkNotNull(value); |
| |
| if (value.isEmpty()) { |
| final String msg = "Specified IPv6 cannot be an empty string"; |
| throw new IllegalArgumentException(msg); |
| } |
| InetAddress addr = null; |
| try { |
| addr = InetAddresses.forString(value); |
| } catch (IllegalArgumentException e) { |
| final String msg = "Invalid IPv6 address string: " + value; |
| throw new IllegalArgumentException(msg); |
| } |
| byte[] bytes = addr.getAddress(); |
| ByteBuffer bb = ByteBuffer.wrap(bytes); |
| this.valueHigh = bb.getLong(); |
| this.valueLow = bb.getLong(); |
| } |
| |
| /** |
| * Gets the IPv6 address as a byte array. |
| * |
| * @return a byte array with the IPv6 address stored in network byte order |
| * (i.e., the most significant byte first). |
| */ |
| public byte[] toOctets() { |
| return ByteBuffer.allocate(BYTE_LENGTH) |
| .putLong(valueHigh).putLong(valueLow).array(); |
| } |
| |
| /** |
| * Creates an IPv6 network mask prefix. |
| * |
| * @param prefixLen the length of the mask prefix. Must be in the interval |
| * [0, 128]. |
| * @return a new IPv6 address that contains a mask prefix of the |
| * specified length |
| */ |
| public static Ip6Address makeMaskPrefix(int prefixLen) { |
| long vh, vl; |
| |
| // Verify the prefix length |
| if ((prefixLen < 0) || (prefixLen > Ip6Address.BIT_LENGTH)) { |
| final String msg = "Invalid IPv6 prefix length: " + prefixLen + |
| ". Must be in the interval [0, 128]."; |
| throw new IllegalArgumentException(msg); |
| } |
| |
| if (prefixLen == 0) { |
| // |
| // NOTE: Apparently, the result of "<< 64" shifting to the left |
| // results in all 1s instead of all 0s, hence we handle it as |
| // a special case. |
| // |
| vh = 0; |
| vl = 0; |
| } else if (prefixLen <= 64) { |
| vh = (0xffffffffffffffffL << (64 - prefixLen)) & 0xffffffffffffffffL; |
| vl = 0; |
| } else { |
| vh = -1L; // All 1s |
| vl = (0xffffffffffffffffL << (128 - prefixLen)) & 0xffffffffffffffffL; |
| } |
| return new Ip6Address(vh, vl); |
| } |
| |
| /** |
| * Creates an IPv6 address by masking it with a network mask of given |
| * mask length. |
| * |
| * @param addr the address to mask |
| * @param prefixLen the length of the mask prefix. Must be in the interval |
| * [0, 128]. |
| * @return a new IPv6 address that is masked with a mask prefix of the |
| * specified length |
| */ |
| public static Ip6Address makeMaskedAddress(final Ip6Address addr, |
| int prefixLen) { |
| Ip6Address mask = Ip6Address.makeMaskPrefix(prefixLen); |
| long vh = addr.valueHigh & mask.valueHigh; |
| long vl = addr.valueLow & mask.valueLow; |
| |
| return new Ip6Address(vh, vl); |
| } |
| |
| /** |
| * Gets the value of the higher (more significant) 64 bits of the address. |
| * |
| * @return the value of the higher (more significant) 64 bits of the |
| * address |
| */ |
| public long getValueHigh() { |
| return valueHigh; |
| } |
| |
| /** |
| * Gets the value of the lower (less significant) 64 bits of the address. |
| * |
| * @return the value of the lower (less significant) 64 bits of the |
| * address |
| */ |
| public long getValueLow() { |
| return valueLow; |
| } |
| |
| /** |
| * Converts the IPv6 value to a ':' separated string. |
| * |
| * @return the IPv6 value as a ':' separated string |
| */ |
| @Override |
| public String toString() { |
| ByteBuffer bb = ByteBuffer.allocate(Ip6Address.BYTE_LENGTH); |
| bb.putLong(valueHigh); |
| bb.putLong(valueLow); |
| InetAddress inetAddr = null; |
| try { |
| inetAddr = InetAddress.getByAddress(bb.array()); |
| } catch (UnknownHostException e) { |
| // Should never happen |
| checkState(false, "Internal error: Ip6Address.toString()"); |
| return "::"; |
| } |
| return InetAddresses.toAddrString(inetAddr); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof Ip6Address)) { |
| return false; |
| } |
| Ip6Address other = (Ip6Address) o; |
| return this.valueHigh == other.valueHigh |
| && this.valueLow == other.valueLow; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(valueHigh, valueLow); |
| } |
| |
| @Override |
| public int compareTo(Ip6Address o) { |
| // Compare the high-order 64-bit value |
| if (this.valueHigh != o.valueHigh) { |
| return UnsignedLongs.compare(this.valueHigh, o.valueHigh); |
| } |
| // Compare the low-order 64-bit value |
| if (this.valueLow != o.valueLow) { |
| return UnsignedLongs.compare(this.valueLow, o.valueLow); |
| } |
| return 0; |
| } |
| } |