/* * 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); } } }