blob: 33be226996712d00bb042236ae81df3d7e83c166 [file] [log] [blame]
/*
* 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.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A class representing an IP address.
* TODO: Add support for IPv6 as well.
*/
public final class IpAddress implements Comparable<IpAddress> {
// IP Versions
public enum Version { INET, INET6 };
// lengths of address, in bytes
public static final int INET_BYTE_LENGTH = 4;
public static final int INET_BIT_LENGTH = INET_BYTE_LENGTH * Byte.SIZE;
public static final int INET6_BYTE_LENGTH = 16;
public static final int INET6_BIT_LENGTH = INET6_BYTE_LENGTH * Byte.SIZE;
private final Version version;
private final byte[] octets;
/**
* Constructor for given IP address version and address octets.
*
* @param value the IP address value stored in network byte order
* (i.e., the most significant byte first)
* @param value the IP address value
*/
private IpAddress(Version version, byte[] value) {
this.version = version;
this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
}
/**
* Converts an integer into an IPv4 address.
*
* @param value an integer representing an IPv4 address value
* @return an IP address
*/
public static IpAddress valueOf(int value) {
byte[] bytes =
ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
return new IpAddress(Version.INET, bytes);
}
/**
* Converts a byte array into an IP address.
*
* @param value the IP address value stored in network byte order
* (i.e., the most significant byte first)
* @return an IP address
*/
public static IpAddress valueOf(byte[] value) {
checkNotNull(value);
return new IpAddress(Version.INET, value);
}
/**
* Converts a byte array and a given offset from the beginning of the
* array into an IP address.
* <p>
* The IP address is stored in network byte order (i.e., the most
* significant byte first).
* </p>
* @param value the value to use
* @param offset the offset in bytes from the beginning of the byte array
* @return an IP address
*/
public static IpAddress valueOf(byte[] value, int offset) {
// Verify the arguments
if ((offset < 0) || (offset + INET_BYTE_LENGTH > value.length)) {
String msg;
if (value.length < INET_BYTE_LENGTH) {
msg = "Invalid IPv4 address array: array length: " +
value.length + ". Must be at least " + INET_BYTE_LENGTH;
} else {
msg = "Invalid IPv4 address array: array offset: " +
offset + ". Must be in the interval [0, " +
(value.length - INET_BYTE_LENGTH) + "]";
}
throw new IllegalArgumentException(msg);
}
byte[] bc = Arrays.copyOfRange(value, offset, value.length);
return IpAddress.valueOf(bc);
}
/**
* Converts a dotted-decimal string (x.x.x.x) into an IPv4 address.
*
* @param address an IP address in string form, e.g. "10.0.0.1"
* @return an IP address
*/
public static IpAddress valueOf(String address) {
final String[] net = address.split("\\.");
if (net.length != INET_BYTE_LENGTH) {
String msg = "Malformed IPv4 address string: " + address + "." +
"Address must have four decimal values separated by dots (.)";
throw new IllegalArgumentException(msg);
}
final byte[] bytes = new byte[INET_BYTE_LENGTH];
for (int i = 0; i < INET_BYTE_LENGTH; i++) {
bytes[i] = (byte) Short.parseShort(net[i], 10);
}
return new IpAddress(Version.INET, bytes);
}
/**
* Returns the IP version of this address.
*
* @return the version
*/
public Version version() {
return this.version;
}
/**
* Returns the IP address as a byte array.
*
* @return a byte array
*/
public byte[] toOctets() {
return Arrays.copyOf(this.octets, INET_BYTE_LENGTH);
}
/**
* Returns the integral value of this IP address.
*
* @return the IP address's value as an integer
*/
public int toInt() {
ByteBuffer bb = ByteBuffer.wrap(octets);
return bb.getInt();
}
/**
* Creates an IP network mask prefix.
*
* @param prefixLength the length of the mask prefix. Must be in the
* interval [0, 32] for IPv4
* @return a new IP address that contains a mask prefix of the
* specified length
*/
public static IpAddress makeMaskPrefix(int prefixLength) {
// Verify the prefix length
if ((prefixLength < 0) || (prefixLength > INET_BIT_LENGTH)) {
final String msg = "Invalid IPv4 prefix length: " + prefixLength +
". Must be in the interval [0, 32].";
throw new IllegalArgumentException(msg);
}
long v =
(0xffffffffL << (INET_BIT_LENGTH - prefixLength)) & 0xffffffffL;
return IpAddress.valueOf((int) v);
}
/**
* Creates an IP address by masking it with a network mask of given
* mask length.
*
* @param addr the address to mask
* @param prefixLength the length of the mask prefix. Must be in the
* interval [0, 32] for IPv4
* @return a new IP address that is masked with a mask prefix of the
* specified length
*/
public static IpAddress makeMaskedAddress(final IpAddress addr,
int prefixLength) {
IpAddress mask = IpAddress.makeMaskPrefix(prefixLength);
byte[] net = new byte[INET_BYTE_LENGTH];
// Mask each byte
for (int i = 0; i < INET_BYTE_LENGTH; i++) {
net[i] = (byte) (addr.octets[i] & mask.octets[i]);
}
return IpAddress.valueOf(net);
}
@Override
public int compareTo(IpAddress o) {
Long lv = ((long) this.toInt()) & 0xffffffffL;
Long rv = ((long) o.toInt()) & 0xffffffffL;
return lv.compareTo(rv);
}
@Override
public int hashCode() {
return Objects.hash(version, Arrays.hashCode(octets));
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (getClass() != obj.getClass())) {
return false;
}
IpAddress other = (IpAddress) obj;
return (version == other.version) &&
Arrays.equals(octets, other.octets);
}
@Override
/*
* (non-Javadoc)
* The format is "x.x.x.x" for IPv4 addresses.
*
* @see java.lang.Object#toString()
*/
public String toString() {
final StringBuilder builder = new StringBuilder();
for (final byte b : this.octets) {
if (builder.length() > 0) {
builder.append(".");
}
builder.append(String.format("%d", b & 0xff));
}
return builder.toString();
}
}