blob: 7f50327464310bcc7885fee98d3dbbdbf7d4a4e3 [file] [log] [blame]
/*
* Copyright 2014-present 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.util.Objects;
/**
* A class representing an IP prefix. A prefix consists of an IP address and
* a subnet mask.
* This class is immutable.
* <p>
* NOTE: The stored IP address in the result IP prefix is masked to
* contain zeroes in all bits after the prefix length.
* </p>
*/
public class IpPrefix {
/**
* Longest IPv4 network prefix.
*/
public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
/**
* Longest IPv6 network prefix.
*/
public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
/**
* An IpPrefix that contains all IPv4 multicast addresses.
*/
@Deprecated
public static final IpPrefix MULTICAST_RANGE = IpPrefix.valueOf("224.0.0.0/4");
/**
* An IpPrefix that contains all IPv4 multicast addresses.
*/
public static final IpPrefix IPV4_MULTICAST_PREFIX = IpPrefix.valueOf("224.0.0.0/4");
/**
* An IpPrefix that contains all IPv6 multicast addresses.
*/
public static final IpPrefix IPV6_MULTICAST_PREFIX = IpPrefix.valueOf("ff00::/8");
private final IpAddress address;
private final short prefixLength;
/**
* Constructor for given IP address, and a prefix length.
*
* @param address the IP address
* @param prefixLength the prefix length
* @throws IllegalArgumentException if the prefix length value is invalid
*/
protected IpPrefix(IpAddress address, int prefixLength) {
checkPrefixLength(address.version(), prefixLength);
this.address = IpAddress.makeMaskedAddress(address, prefixLength);
this.prefixLength = (short) prefixLength;
}
/**
* Default constructor for Kryo serialization.
*/
protected IpPrefix() {
this.address = null;
this.prefixLength = 0;
}
/**
* Returns the IP version of the prefix.
*
* @return the IP version of the prefix
*/
public IpAddress.Version version() {
return address.version();
}
/**
* Tests whether the IP version of this prefix is IPv4.
*
* @return true if the IP version of this prefix is IPv4, otherwise false.
*/
public boolean isIp4() {
return address.isIp4();
}
/**
* Tests whether the IP version of this prefix is IPv6.
*
* @return true if the IP version of this prefix is IPv6, otherwise false.
*/
public boolean isIp6() {
return address.isIp6();
}
/**
* Check if this IP prefix is a multicast prefix.
*
* @return true if this prefix a multicast prefix
*/
public boolean isMulticast() {
return isIp4() ?
IPV4_MULTICAST_PREFIX.contains(this.getIp4Prefix()) :
IPV6_MULTICAST_PREFIX.contains(this.getIp6Prefix());
}
/**
* Returns the IP address value of the prefix.
*
* @return the IP address value of the prefix
*/
public IpAddress address() {
return address;
}
/**
* Returns the IP address prefix length.
*
* @return the IP address prefix length
*/
public int prefixLength() {
return prefixLength;
}
/**
* Gets the {@link Ip4Prefix} view of the IP prefix.
*
* @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
* otherwise null
*/
public Ip4Prefix getIp4Prefix() {
if (!isIp4()) {
return null;
}
// Return this object itself if it is already instance of Ip4Prefix
if (this instanceof Ip4Prefix) {
return (Ip4Prefix) this;
}
return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
}
/**
* Gets the {@link Ip6Prefix} view of the IP prefix.
*
* @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
* otherwise null
*/
public Ip6Prefix getIp6Prefix() {
if (!isIp6()) {
return null;
}
// Return this object itself if it is already instance of Ip6Prefix
if (this instanceof Ip6Prefix) {
return (Ip6Prefix) this;
}
return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
}
/**
* Converts an integer and a prefix length into an IPv4 prefix.
*
* @param address an integer representing the IPv4 address
* @param prefixLength the prefix length
* @return an IP prefix
* @throws IllegalArgumentException if the prefix length value is invalid
*/
public static IpPrefix valueOf(int address, int prefixLength) {
return new IpPrefix(IpAddress.valueOf(address), prefixLength);
}
/**
* Converts a byte array and a prefix length into an IP prefix.
*
* @param version the IP address version
* @param address the IP address value stored in network byte order
* @param prefixLength the prefix length
* @return an IP prefix
* @throws IllegalArgumentException if the prefix length value is invalid
*/
public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
int prefixLength) {
return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
}
/**
* Converts an IP address and a prefix length into an IP prefix.
*
* @param address the IP address
* @param prefixLength the prefix length
* @return an IP prefix
* @throws IllegalArgumentException if the prefix length value is invalid
*/
public static IpPrefix valueOf(IpAddress address, int prefixLength) {
return new IpPrefix(address, prefixLength);
}
/**
* Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
* "1111:2222::/64") into an IP prefix.
*
* @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
* "1111:2222::/64")
* @return an IP prefix
* @throws IllegalArgumentException if the arguments are invalid
*/
public static IpPrefix valueOf(String address) {
final String[] parts = address.split("/");
if (parts.length != 2) {
String msg = "Malformed IP prefix string: " + address + ". " +
"Address must take form \"x.x.x.x/y\" or " +
"\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
throw new IllegalArgumentException(msg);
}
IpAddress ipAddress = IpAddress.valueOf(parts[0]);
int prefixLength = Integer.parseInt(parts[1]);
return new IpPrefix(ipAddress, prefixLength);
}
/**
* Determines whether a given IP prefix is contained within this prefix.
*
* @param other the IP prefix to test
* @return true if the other IP prefix is contained in this prefix,
* otherwise false
*/
public boolean contains(IpPrefix other) {
if (version() != other.version()) {
return false;
}
if (this.prefixLength > other.prefixLength) {
return false; // This prefix has smaller prefix size
}
//
// Mask the other address with my prefix length.
// If the other prefix is within this prefix, the masked address must
// be same as the address of this prefix.
//
IpAddress maskedAddr =
IpAddress.makeMaskedAddress(other.address, this.prefixLength);
return this.address.equals(maskedAddr);
}
/**
* Determines whether a given IP address is contained within this prefix.
*
* @param other the IP address to test
* @return true if the IP address is contained in this prefix, otherwise
* false
*/
public boolean contains(IpAddress other) {
if (version() != other.version()) {
return false;
}
//
// Mask the other address with my prefix length.
// If the other prefix is within this prefix, the masked address must
// be same as the address of this prefix.
//
IpAddress maskedAddr =
IpAddress.makeMaskedAddress(other, this.prefixLength);
return this.address.equals(maskedAddr);
}
@Override
public int hashCode() {
return Objects.hash(address, prefixLength);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (!(obj instanceof IpPrefix))) {
return false;
}
IpPrefix other = (IpPrefix) obj;
return ((prefixLength == other.prefixLength) &&
address.equals(other.address));
}
@Override
/*
* (non-Javadoc)
* The format is "x.x.x.x/y" for IPv4 prefixes.
*
* @see java.lang.Object#toString()
*/
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(address.toString());
builder.append("/");
builder.append(String.format("%d", prefixLength));
return builder.toString();
}
/**
* Checks whether the prefix length is valid.
*
* @param version the IP address version
* @param prefixLength the prefix length value to check
* @throws IllegalArgumentException if the prefix length value is invalid
*/
private static void checkPrefixLength(IpAddress.Version version,
int prefixLength) {
int maxPrefixLen = 0;
switch (version) {
case INET:
maxPrefixLen = MAX_INET_MASK_LENGTH;
break;
case INET6:
maxPrefixLen = MAX_INET6_MASK_LENGTH;
break;
default:
String msg = "Invalid IP version " + version;
throw new IllegalArgumentException(msg);
}
if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
String msg = "Invalid prefix length " + prefixLength + ". " +
"The value must be in the interval [0, " +
maxPrefixLen + "]";
throw new IllegalArgumentException(msg);
}
}
}